From 61aee5e906d8e11d8b2f004df6867e0c2cd0cac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 6 Nov 2017 11:49:38 +0100 Subject: [PATCH 001/156] add montly discharge to improve soc calculation --- .../AvoidTotalDischargeController.java | 377 ++++++++++-------- .../asymmetric/avoidtotaldischarge/Ess.java | 136 +++---- 2 files changed, 273 insertions(+), 240 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index dfb99303e4a..6f263845d0f 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -1,173 +1,204 @@ -/******************************************************************************* - * 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.asymmetric.avoidtotaldischarge; - -import java.util.Optional; -import java.util.Set; - -import io.openems.api.channel.ConfigChannel; -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.InvalidValueException; -import io.openems.api.exception.WriteChannelException; -import io.openems.impl.controller.asymmetric.avoidtotaldischarge.Ess.State; - -@ThingInfo(title = "Avoid total discharge of battery (Asymmetric)", description = "Makes sure the battery is not going into critically low state of charge. For asymmetric Ess.") -public class AvoidTotalDischargeController extends Controller { - - /* - * Constructors - */ - public AvoidTotalDischargeController() { - super(); - } - - public AvoidTotalDischargeController(String id) { - super(id); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) - public final ConfigChannel> esss = new ConfigChannel>("esss", this); - @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") - public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); - - /* - * Methods - */ - @Override - public void run() { - try { - for (Ess ess : esss.value()) { - /* - * Calculate SetActivePower according to MinSoc - */ - switch (ess.currentState) { - case CHARGESOC: - if (ess.soc.value() > ess.minSoc.value()) { - ess.currentState = State.MINSOC; - } else { - try { - Optional currentMinValueL1 = ess.setActivePowerL1.writeMin(); - if (currentMinValueL1.isPresent() && currentMinValueL1.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePowerL1=Max[" + currentMinValueL1.get() / 5 + "]"); - ess.setActivePowerL1.pushWriteMax(currentMinValueL1.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePowerL1=Max[-1000 W]"); - ess.setActivePowerL1.pushWriteMax(-1000L); - } - } catch (WriteChannelException e) { - log.error("Unable to set ActivePowerL1: " + e.getMessage()); - } - try { - Optional currentMinValueL2 = ess.setActivePowerL2.writeMin(); - if (currentMinValueL2.isPresent() && currentMinValueL2.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePowerL2=Max[" + currentMinValueL2.get() / 5 + "]"); - ess.setActivePowerL2.pushWriteMax(currentMinValueL2.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePowerL2=Max[-1000 W]"); - ess.setActivePowerL2.pushWriteMax(-1000L); - } - } catch (WriteChannelException e) { - log.error("Unable to set ActivePowerL2: " + e.getMessage()); - } - try { - Optional currentMinValueL3 = ess.setActivePowerL3.writeMin(); - if (currentMinValueL3.isPresent() && currentMinValueL3.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePowerL3=Max[" + currentMinValueL3.get() / 5 + "]"); - ess.setActivePowerL3.pushWriteMax(currentMinValueL3.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePowerL3=Max[-1000 W]"); - ess.setActivePowerL3.pushWriteMax(-1000L); - } - } catch (WriteChannelException e) { - log.error("Unable to set ActivePowerL3: " + e.getMessage()); - } - } - break; - case MINSOC: - if (ess.soc.value() < ess.chargeSoc.value()) { - ess.currentState = State.CHARGESOC; - } else if (ess.soc.value() >= ess.minSoc.value() + 5) { - ess.currentState = State.NORMAL; - } else { - try { - long maxPower = 0; - if (!ess.setActivePowerL1.writeMax().isPresent() - || maxPower < ess.setActivePowerL1.writeMax().get()) { - ess.setActivePowerL1.pushWriteMax(maxPower); - } - if (!ess.setActivePowerL2.writeMax().isPresent() - || maxPower < ess.setActivePowerL2.writeMax().get()) { - ess.setActivePowerL2.pushWriteMax(maxPower); - } - if (!ess.setActivePowerL3.writeMax().isPresent() - || maxPower < ess.setActivePowerL3.writeMax().get()) { - ess.setActivePowerL3.pushWriteMax(maxPower); - } - } catch (WriteChannelException e) { - log.error(ess.id() + "Failed to set Max allowed power.", e); - } - } - break; - case NORMAL: - if (ess.soc.value() <= ess.minSoc.value()) { - ess.currentState = State.MINSOC; - } else if (ess.soc.value() >= 99 && ess.allowedCharge.value() == 0 - && ess.systemState.labelOptional().equals(Optional.of(EssNature.START))) { - ess.currentState = State.FULL; - } - break; - case FULL: - try { - ess.setActivePowerL1.pushWriteMin(0L); - } catch (WriteChannelException e) { - log.error("Unable to set ActivePowerL1: " + e.getMessage()); - } - try { - ess.setActivePowerL2.pushWriteMin(0L); - } catch (WriteChannelException e) { - log.error("Unable to set ActivePowerL2: " + e.getMessage()); - } - try { - ess.setActivePowerL3.pushWriteMin(0L); - } catch (WriteChannelException e) { - log.error("Unable to set ActivePowerL3: " + e.getMessage()); - } - if (ess.soc.value() < maxSoc.value()) { - ess.currentState = State.NORMAL; - } - break; - } - } - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - -} +/******************************************************************************* + * 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.asymmetric.avoidtotaldischarge; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Optional; +import java.util.Set; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.ConfigChannel; +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.InvalidValueException; +import io.openems.api.exception.WriteChannelException; +import io.openems.impl.controller.asymmetric.avoidtotaldischarge.Ess.State; + +@ThingInfo(title = "Avoid total discharge of battery (Asymmetric)", description = "Makes sure the battery is not going into critically low state of charge. For asymmetric Ess.") +public class AvoidTotalDischargeController extends Controller implements ChannelChangeListener { + + /* + * Constructors + */ + public AvoidTotalDischargeController() { + super(); + } + + public AvoidTotalDischargeController(String id) { + super(id); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) + public final ConfigChannel> esss = new ConfigChannel>("esss", this); + @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") + public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); + @ChannelInfo(title = "Last Discharge", description = "Last Time, the ess was discharged completely.", type = Long.class,defaultValue = "0") + public final ConfigChannel lastDischarge = new ConfigChannel("lastDischarge", this).addChangeListener(this); + @ChannelInfo(title = "Enable Montly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") + public final ConfigChannel enableMonthlyDischarge = new ConfigChannel("EnableMonthlyDischarge",this); + + private LocalDate lastDischargeDate; + + /* + * Methods + */ + @Override + public void run() { + try { + for (Ess ess : esss.value()) { + /* + * Calculate SetActivePower according to MinSoc + */ + switch (ess.currentState) { + case CHARGESOC: + if (ess.soc.value() > ess.minSoc.value()) { + ess.currentState = State.MINSOC; + } else { + try { + Optional currentMinValueL1 = ess.setActivePowerL1.writeMin(); + if (currentMinValueL1.isPresent() && currentMinValueL1.get() < 0) { + // Force Charge with minimum of MaxChargePower/5 + log.info("Force charge. Set ActivePowerL1=Max[" + currentMinValueL1.get() / 5 + "]"); + ess.setActivePowerL1.pushWriteMax(currentMinValueL1.get() / 5); + } else { + log.info("Avoid discharge. Set ActivePowerL1=Max[-1000 W]"); + ess.setActivePowerL1.pushWriteMax(-1000L); + } + } catch (WriteChannelException e) { + log.error("Unable to set ActivePowerL1: " + e.getMessage()); + } + try { + Optional currentMinValueL2 = ess.setActivePowerL2.writeMin(); + if (currentMinValueL2.isPresent() && currentMinValueL2.get() < 0) { + // Force Charge with minimum of MaxChargePower/5 + log.info("Force charge. Set ActivePowerL2=Max[" + currentMinValueL2.get() / 5 + "]"); + ess.setActivePowerL2.pushWriteMax(currentMinValueL2.get() / 5); + } else { + log.info("Avoid discharge. Set ActivePowerL2=Max[-1000 W]"); + ess.setActivePowerL2.pushWriteMax(-1000L); + } + } catch (WriteChannelException e) { + log.error("Unable to set ActivePowerL2: " + e.getMessage()); + } + try { + Optional currentMinValueL3 = ess.setActivePowerL3.writeMin(); + if (currentMinValueL3.isPresent() && currentMinValueL3.get() < 0) { + // Force Charge with minimum of MaxChargePower/5 + log.info("Force charge. Set ActivePowerL3=Max[" + currentMinValueL3.get() / 5 + "]"); + ess.setActivePowerL3.pushWriteMax(currentMinValueL3.get() / 5); + } else { + log.info("Avoid discharge. Set ActivePowerL3=Max[-1000 W]"); + ess.setActivePowerL3.pushWriteMax(-1000L); + } + } catch (WriteChannelException e) { + log.error("Unable to set ActivePowerL3: " + e.getMessage()); + } + } + break; + case MINSOC: + if (ess.soc.value() < ess.chargeSoc.value()) { + ess.currentState = State.CHARGESOC; + } else if (ess.soc.value() >= ess.minSoc.value() + 5) { + ess.currentState = State.NORMAL; + }else if(lastDischargeDate != null && lastDischargeDate.plusMonths(1).isBefore(LocalDate.now()) && enableMonthlyDischarge.valueOptional().isPresent() && enableMonthlyDischarge.valueOptional().get()) { + ess.currentState = State.EMPTY; + } else { + try { + long maxPower = 0; + if (!ess.setActivePowerL1.writeMax().isPresent() + || maxPower < ess.setActivePowerL1.writeMax().get()) { + ess.setActivePowerL1.pushWriteMax(maxPower); + } + if (!ess.setActivePowerL2.writeMax().isPresent() + || maxPower < ess.setActivePowerL2.writeMax().get()) { + ess.setActivePowerL2.pushWriteMax(maxPower); + } + if (!ess.setActivePowerL3.writeMax().isPresent() + || maxPower < ess.setActivePowerL3.writeMax().get()) { + ess.setActivePowerL3.pushWriteMax(maxPower); + } + } catch (WriteChannelException e) { + log.error(ess.id() + "Failed to set Max allowed power.", e); + } + } + break; + case NORMAL: + if (ess.soc.value() <= ess.minSoc.value()) { + ess.currentState = State.MINSOC; + } else if (ess.soc.value() >= 99 && ess.allowedCharge.value() == 0 + && ess.systemState.labelOptional().equals(Optional.of(EssNature.START))) { + ess.currentState = State.FULL; + } + break; + case FULL: + try { + ess.setActivePowerL1.pushWriteMin(0L); + } catch (WriteChannelException e) { + log.error("Unable to set ActivePowerL1: " + e.getMessage()); + } + try { + ess.setActivePowerL2.pushWriteMin(0L); + } catch (WriteChannelException e) { + log.error("Unable to set ActivePowerL2: " + e.getMessage()); + } + try { + ess.setActivePowerL3.pushWriteMin(0L); + } catch (WriteChannelException e) { + log.error("Unable to set ActivePowerL3: " + e.getMessage()); + } + if (ess.soc.value() < maxSoc.value()) { + ess.currentState = State.NORMAL; + } + break; + case EMPTY: + if(ess.allowedDischarge.value() == 0) { + //Ess is Empty set Date and charge to minSoc + lastDischarge.updateValue(System.currentTimeMillis(), true); + ess.currentState = State.CHARGESOC; + } + break; + } + } + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } + } + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if(this.lastDischarge.equals(channel)) { + if(newValue.isPresent()) { + lastDischargeDate = Instant.ofEpochMilli((long) newValue.get()).atZone(ZoneId.systemDefault()).toLocalDate(); + }else { + lastDischargeDate = null; + } + } + } + +} diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java index 7b0fb84cda2..d35dee071d2 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java @@ -1,67 +1,69 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.asymmetric.avoidtotaldischarge; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.AsymmetricEssNature; -import io.openems.api.exception.InvalidValueException; - -@IsThingMap(type = AsymmetricEssNature.class) -public class Ess extends ThingMap { - public ReadChannel soc; - public ReadChannel minSoc; - public ReadChannel chargeSoc; - public WriteChannel setActivePowerL1; - public WriteChannel setActivePowerL2; - public WriteChannel setActivePowerL3; - public WriteChannel setReactivePowerL1; - public WriteChannel setReactivePowerL2; - public WriteChannel setReactivePowerL3; - public ReadChannel systemState; - public ReadChannel allowedCharge; - public State currentState = State.NORMAL; - - public enum State { - NORMAL, MINSOC, CHARGESOC, FULL; - } - - public Ess(AsymmetricEssNature ess) { - super(ess); - minSoc = ess.minSoc().required(); - chargeSoc = ess.chargeSoc().required(); - setActivePowerL1 = ess.setActivePowerL1().required(); - setActivePowerL2 = ess.setActivePowerL2().required(); - setActivePowerL3 = ess.setActivePowerL3().required(); - setReactivePowerL1 = ess.setReactivePowerL1().required(); - setReactivePowerL2 = ess.setReactivePowerL2().required(); - setReactivePowerL3 = ess.setReactivePowerL3().required(); - soc = ess.soc().required(); - systemState = ess.systemState().required(); - allowedCharge = ess.allowedCharge().required(); - } - - public long useableSoc() throws InvalidValueException { - return soc.value() - minSoc.value(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.asymmetric.avoidtotaldischarge; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.AsymmetricEssNature; +import io.openems.api.exception.InvalidValueException; + +@IsThingMap(type = AsymmetricEssNature.class) +public class Ess extends ThingMap { + public ReadChannel soc; + public ReadChannel minSoc; + public ReadChannel chargeSoc; + public WriteChannel setActivePowerL1; + public WriteChannel setActivePowerL2; + public WriteChannel setActivePowerL3; + public WriteChannel setReactivePowerL1; + public WriteChannel setReactivePowerL2; + public WriteChannel setReactivePowerL3; + public ReadChannel systemState; + public ReadChannel allowedCharge; + public ReadChannel allowedDischarge; + public State currentState = State.NORMAL; + + public enum State { + NORMAL, MINSOC, CHARGESOC, FULL,EMPTY; + } + + public Ess(AsymmetricEssNature ess) { + super(ess); + minSoc = ess.minSoc().required(); + chargeSoc = ess.chargeSoc().required(); + setActivePowerL1 = ess.setActivePowerL1().required(); + setActivePowerL2 = ess.setActivePowerL2().required(); + setActivePowerL3 = ess.setActivePowerL3().required(); + setReactivePowerL1 = ess.setReactivePowerL1().required(); + setReactivePowerL2 = ess.setReactivePowerL2().required(); + setReactivePowerL3 = ess.setReactivePowerL3().required(); + soc = ess.soc().required(); + systemState = ess.systemState().required(); + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + } + + public long useableSoc() throws InvalidValueException { + return soc.value() - minSoc.value(); + } +} From a6fcdc98afd03dca9abe2e03d80e59b5f89e2686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 6 Nov 2017 11:52:27 +0100 Subject: [PATCH 002/156] add Monthly Discharge to improve Soc calculation --- .../AvoidTotalDischargeController.java | 33 +++- .../symmetric/avoidtotaldischarge/Ess.java | 154 +++++++++--------- 2 files changed, 109 insertions(+), 78 deletions(-) diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 0bdc89fa738..8b39d8fa47b 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -20,9 +20,14 @@ *******************************************************************************/ package io.openems.impl.controller.symmetric.avoidtotaldischarge; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.Optional; import java.util.Set; +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; import io.openems.api.controller.Controller; import io.openems.api.device.nature.ess.EssNature; @@ -33,7 +38,7 @@ import io.openems.impl.controller.symmetric.avoidtotaldischarge.Ess.State; @ThingInfo(title = "Avoid total discharge of battery (Symmetric)", description = "Makes sure the battery is not going into critically low state of charge. For symmetric Ess.") -public class AvoidTotalDischargeController extends Controller { +public class AvoidTotalDischargeController extends Controller implements ChannelChangeListener { /* * Constructors @@ -53,6 +58,12 @@ public AvoidTotalDischargeController(String thingId) { public final ConfigChannel> esss = new ConfigChannel>("esss", this); @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); + @ChannelInfo(title = "Last Discharge", description = "Last Time, the ess was discharged completely.", type = Long.class,defaultValue = "0") + public final ConfigChannel lastDischarge = new ConfigChannel("lastDischarge", this).addChangeListener(this); + @ChannelInfo(title = "Enable Montly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") + public final ConfigChannel enableMonthlyDischarge = new ConfigChannel("EnableMonthlyDischarge",this); + + private LocalDate lastDischargeDate; /* * Methods @@ -92,6 +103,8 @@ public void run() { ess.currentState = State.CHARGESOC; } else if (ess.soc.value() >= ess.minSoc.value() + 5) { ess.currentState = State.NORMAL; + }else if(lastDischargeDate != null && lastDischargeDate.plusMonths(1).isBefore(LocalDate.now()) && enableMonthlyDischarge.valueOptional().isPresent() && enableMonthlyDischarge.valueOptional().get()) { + ess.currentState = State.EMPTY; } else { try { long maxPower = 0; @@ -123,6 +136,13 @@ public void run() { ess.currentState = State.NORMAL; } break; + case EMPTY: + if(ess.allowedDischarge.value() == 0) { + //Ess is Empty set Date and charge to minSoc + lastDischarge.updateValue(System.currentTimeMillis(), true); + ess.currentState = State.CHARGESOC; + } + break; } } catch (InvalidValueException e) { log.error(e.getMessage()); @@ -132,4 +152,15 @@ public void run() { log.error("no ess configured"+e.getMessage()); } } + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if(this.lastDischarge.equals(channel)) { + if(newValue.isPresent()) { + lastDischargeDate = Instant.ofEpochMilli((long) newValue.get()).atZone(ZoneId.systemDefault()).toLocalDate(); + }else { + lastDischargeDate = null; + } + } + } } diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java index c4c806cb8fe..a92a6bbb491 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java @@ -1,77 +1,77 @@ -/******************************************************************************* - * 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.symmetric.avoidtotaldischarge; - -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.hysteresis.Hysteresis; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final ReadChannel soc; - public final ReadChannel systemState; - public int maxPowerPercent = 100; - public final ReadChannel allowedDischarge; - public final ReadChannel allowedCharge; - public final ReadChannel chargeSoc; - public Hysteresis socMinHysteresis; - public State currentState = State.NORMAL; - - public enum State { - NORMAL, MINSOC, CHARGESOC, FULL; - } - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - systemState = ess.systemState().required(); - soc = ess.soc().required(); - minSoc = ess.minSoc().required(); - allowedDischarge = ess.allowedDischarge().required(); - allowedCharge = ess.allowedCharge().required(); - chargeSoc = ess.chargeSoc().required(); - ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (minSoc.valueOptional().isPresent() && chargeSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(chargeSoc.valueOptional().get(), minSoc.valueOptional().get()); - } else if (minSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 3, minSoc.valueOptional().get()); - } - } - }; - minSoc.addChangeListener(hysteresisCreator); - chargeSoc.addChangeListener(hysteresisCreator); - - hysteresisCreator.channelChanged(null, null, null); - } -} +/******************************************************************************* + * 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.symmetric.avoidtotaldischarge; + +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.core.utilities.hysteresis.Hysteresis; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final WriteChannel setActivePower; + public final ReadChannel soc; + public final ReadChannel systemState; + public int maxPowerPercent = 100; + public final ReadChannel allowedDischarge; + public final ReadChannel allowedCharge; + public final ReadChannel chargeSoc; + public Hysteresis socMinHysteresis; + public State currentState = State.NORMAL; + + public enum State { + NORMAL, MINSOC, CHARGESOC, FULL, EMPTY; + } + + public Ess(SymmetricEssNature ess) { + super(ess); + setActivePower = ess.setActivePower().required(); + systemState = ess.systemState().required(); + soc = ess.soc().required(); + minSoc = ess.minSoc().required(); + allowedDischarge = ess.allowedDischarge().required(); + allowedCharge = ess.allowedCharge().required(); + chargeSoc = ess.chargeSoc().required(); + ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if (minSoc.valueOptional().isPresent() && chargeSoc.valueOptional().isPresent()) { + socMinHysteresis = new Hysteresis(chargeSoc.valueOptional().get(), minSoc.valueOptional().get()); + } else if (minSoc.valueOptional().isPresent()) { + socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 3, minSoc.valueOptional().get()); + } + } + }; + minSoc.addChangeListener(hysteresisCreator); + chargeSoc.addChangeListener(hysteresisCreator); + + hysteresisCreator.channelChanged(null, null, null); + } +} From d4eb3bb9a939cfa6ca07fee26d4278a42cf8f2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 6 Nov 2017 12:07:25 +0100 Subject: [PATCH 003/156] add DebugChannel --- .../AvoidTotalDischargeController.java | 1 + .../asymmetric/avoidtotaldischarge/Ess.java | 15 ++++++++++++++- .../AvoidTotalDischargeController.java | 1 + .../symmetric/avoidtotaldischarge/Ess.java | 15 ++++++++++++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 6f263845d0f..031ac4c4b70 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -75,6 +75,7 @@ public void run() { /* * Calculate SetActivePower according to MinSoc */ + ess.stateMachineState.setValue(ess.currentState.value()); switch (ess.currentState) { case CHARGESOC: if (ess.soc.value() > ess.minSoc.value()) { diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java index d35dee071d2..32bdddd0ea3 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java @@ -20,6 +20,7 @@ *******************************************************************************/ package io.openems.impl.controller.asymmetric.avoidtotaldischarge; +import io.openems.api.channel.DebugChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.WriteChannel; import io.openems.api.controller.IsThingMap; @@ -42,9 +43,20 @@ public class Ess extends ThingMap { public ReadChannel allowedCharge; public ReadChannel allowedDischarge; public State currentState = State.NORMAL; + public DebugChannel stateMachineState; public enum State { - NORMAL, MINSOC, CHARGESOC, FULL,EMPTY; + NORMAL(0), MINSOC(1), CHARGESOC(2), FULL(3),EMPTY(4); + + private final int value; + + State(int value){ + this.value = value; + } + + public int value() { + return this.value; + } } public Ess(AsymmetricEssNature ess) { @@ -61,6 +73,7 @@ public Ess(AsymmetricEssNature ess) { systemState = ess.systemState().required(); allowedCharge = ess.allowedCharge().required(); allowedDischarge = ess.allowedDischarge().required(); + stateMachineState = new DebugChannel<>("AvoidTotalDischargeState", ess); } public long useableSoc() throws InvalidValueException { diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 8b39d8fa47b..18c2d891faa 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -76,6 +76,7 @@ public void run() { /* * Calculate SetActivePower according to MinSoc */ + ess.stateMachineState.setValue(ess.currentState.value()); switch (ess.currentState) { case CHARGESOC: if (ess.soc.value() > ess.minSoc.value()) { diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java index a92a6bbb491..61c91d3f94b 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java @@ -24,6 +24,7 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.DebugChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.WriteChannel; import io.openems.api.controller.IsThingMap; @@ -44,9 +45,20 @@ public class Ess extends ThingMap { public final ReadChannel chargeSoc; public Hysteresis socMinHysteresis; public State currentState = State.NORMAL; + public DebugChannel stateMachineState; public enum State { - NORMAL, MINSOC, CHARGESOC, FULL, EMPTY; + NORMAL(0), MINSOC(1), CHARGESOC(2), FULL(3),EMPTY(4); + + private final int value; + + State(int value){ + this.value = value; + } + + public int value() { + return this.value; + } } public Ess(SymmetricEssNature ess) { @@ -58,6 +70,7 @@ public Ess(SymmetricEssNature ess) { allowedDischarge = ess.allowedDischarge().required(); allowedCharge = ess.allowedCharge().required(); chargeSoc = ess.chargeSoc().required(); + stateMachineState = new DebugChannel<>("AvoidTotalDischargeState", ess); ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { @Override From 41cae57a56fe60287925d39188f1229f558d4462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 6 Nov 2017 13:42:54 +0100 Subject: [PATCH 004/156] fix typo --- .../avoidtotaldischarge/AvoidTotalDischargeController.java | 2 +- .../avoidtotaldischarge/AvoidTotalDischargeController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 031ac4c4b70..c4fbf130784 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -60,7 +60,7 @@ public AvoidTotalDischargeController(String id) { public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); @ChannelInfo(title = "Last Discharge", description = "Last Time, the ess was discharged completely.", type = Long.class,defaultValue = "0") public final ConfigChannel lastDischarge = new ConfigChannel("lastDischarge", this).addChangeListener(this); - @ChannelInfo(title = "Enable Montly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") + @ChannelInfo(title = "Enable Monthly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") public final ConfigChannel enableMonthlyDischarge = new ConfigChannel("EnableMonthlyDischarge",this); private LocalDate lastDischargeDate; diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 18c2d891faa..c8a0a8305a3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -60,7 +60,7 @@ public AvoidTotalDischargeController(String thingId) { public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); @ChannelInfo(title = "Last Discharge", description = "Last Time, the ess was discharged completely.", type = Long.class,defaultValue = "0") public final ConfigChannel lastDischarge = new ConfigChannel("lastDischarge", this).addChangeListener(this); - @ChannelInfo(title = "Enable Montly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") + @ChannelInfo(title = "Enable Monthly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") public final ConfigChannel enableMonthlyDischarge = new ConfigChannel("EnableMonthlyDischarge",this); private LocalDate lastDischargeDate; From f6149a5a1449ab10f96bf06c3414d4886e5bc412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 16 Jan 2018 15:16:42 +0100 Subject: [PATCH 005/156] add JTS library see also https://sourceforge.net/projects/jts-topo-suite/ --- edge/.classpath | 62 +- edge/lib/jts-1.14-sources.jar | Bin 0 -> 955341 bytes edge/lib/jts-1.14.jar | Bin 0 -> 831241 bytes .../core/utilities/power/SVGWriter.java | 611 ++++++++++++++++++ 4 files changed, 643 insertions(+), 30 deletions(-) create mode 100644 edge/lib/jts-1.14-sources.jar create mode 100644 edge/lib/jts-1.14.jar create mode 100644 edge/src/io/openems/core/utilities/power/SVGWriter.java diff --git a/edge/.classpath b/edge/.classpath index 10569758c03..8577b7e5109 100644 --- a/edge/.classpath +++ b/edge/.classpath @@ -1,30 +1,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edge/lib/jts-1.14-sources.jar b/edge/lib/jts-1.14-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..dcd617c0ab1c4be719013b32c745a20420f63733 GIT binary patch literal 955341 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 z zEZ3LL*XG?DdOQ3-8imvH&X)6X*KpgNOAI6D)7gu$gmCtBoo-1BK2`4W5#2kcYTfiA zvtXEedXCx=6w{yU3VLwk6@s3urGju)3c>Z#;Ht2PI8x;cUT_O3R}5u}=d23a?Z<^; zRq6#xIw}JtD&|K~xZS_a&8kOsGabzU73zQZm2(APHGOcVZIaF%)P|$A3JnC#z|_RS z&Zogz?DcdTEnq>eO6MfzQs(v&KxK;3991H*;=cwNgWJ~0oGAF|eBSW3(TqNZu{g*7 z^E~3X}2yJf~ zx8x=uPd3xEGf@0>F0BCX!Af#gPjq+|80 z*Cf-aiy`ASeG=weP|oSzcdPQOT$ydcTSAuUsI8Xi5uV=E4^6J{3Ot|>AKhwS)j#`~ z(aZ{XkU|=xteC~e2~A%ha{+Twufy?K)Mt(;jHJHxAKY2DPodSNm_+jpn_BW|?n-Fu zAWj{n3#u>msP%kM+~j8%@+5}0ANAq+9!5R32nfKmTj_d+y$QJ%pKL5CDN4EXp48Yn zZsF{$i%w-@9S7sNn$eoHCEow7nBQ6VHg&*g56BR%TQAe={0f?1%�s4lNe|R-2c| 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 z%0D2X$6qVyXrjQ#0GX(@HDa*QN-H|j6#R(mWDQ$_`mJlYYu&5VrGM~2?av4XDT?ba zd*m6YrB2a`!Wq_Br1zzG^mh_JQW4Pgg1MhwLgTBu=Rd>op)K{#k7-7ZTXHT&b;bWM zAN~x2gT;6r(folYa^=D+Lr%kR0%-xD&8uQSSfz{5PUc1zny0;n&qk z|78}a{wJ=^|L^8BYuY+5N+5m9q5c%?SG1ToploW|amtjjdJJmINeshjmv3%lFP4-4~6%+5fB`ir>MIL4*0pj4&XUhYx!L zAEM4;IzR#j6UAz#J16+3_v81|(hUbB%Lw&AI1SIHSx%+Wj5|j3+oXs9`al~YP)-6F zrN-Cys__^j9s$_}rHcdNA@*hOHi$@OFxQhS{}togf)QxQ4hMcq4F3~|pW@0h)2#Jo*0qpW0RlN_Kd7@qjyV}SGe{N75Fbd;LA z+f0s?VT*w2E-0sFat3T1?l{Jebpc^?7KU^X>S9k31N0r(0N zLI+xiRnN0mPb2siPM20@fzwaaW_tD!Z|ERK1-UQ{@5zcbuA><%L)zxE$1EYT6Z%Jt zGIW3Yt~F8lqYHMBQR z<$XTG?Gn}u@{ob0Nf^*0%alH<$x|b#`BDepz5cdf{Q~z{cQA7RU`p8+{;7rJ4Fr^P z!acwa-hNsdvb*C;;TjKAA^{W?xK}rN&Px9zy4TOm*9N89>SeQ^G?ip!0)8Z^?Jd7= zGT45a!OUE|Y0jB5oevGwqzBsHD86NG7@maVJIu=T*$}^sdlK@LAk*HC2qYuFbme;_ z&Gy#Ac!J!bly{@^IQM~2$OlO@UwHEJQ+}^_nes;lO~Whrd8aBpw@Beukn7l8TaWXP zUd^&UWaSU=cFqJ4tp~iW>n7F2~_qYSqeH?n_ zL1Cvm61c^1fB!P+31QCyCp(9kmWvX7s~y~B#lc#{$7v8e?q2~n)5tRtyS_`D?Hn4u z=t7OF;(PkdTL~xq)h0i0`8bKj#7NMAV}R*ao5cH3hKiPxBd7QQ#HB%fs>0*{v$lM9 zwS6QQM62qt#{Tgv8#bDNpgDy6G+T--;ao1U5WTzL;E=f0kiG#YZMO_IoPF7f*g|;~ zez8hOV$+>6Pz1D9Au*DDs*#I~qfGVtL$GEByPGH4gB%Yu)hJGfLS&9sp$kiw2V@-+ z9&1!hxkxX@C{Os%r!G8& z1R5tU`9xZcC{$#X6aaJo6TG?iHvs#VWKbMvX^?Ax<^qLxiN@!!`%}Ig`ekW+oGMn^ zXY`*K2w=dybG?=x)E5o!6p&5tJC(FVI)0-`F8xP}v8t8vm|i|S?`YmoMN-&E^;tHz=I zN;OYNtMow5?;Y`A=>wWt{H~0?iDDk2~<-c~7hq1_i=5JS3 ztW)ikntIkG7_070T`K9R%Ce$Oi7Brd{}oh7tdqtX1@vqOz?jv=wH^?tJ>M&%R%WS1 zs9_cdi5Qj~fZ3l2N+kq|?G;B?&i!Q+3PX#L%s%NAAyQM)Z;%k{LBl|vut!>f#-l46 z^&=ZN`hE?rM}zIDc?wPMjY-;Ax> zS-{e#dlJo%Tf)}8F$=m)y=2at)NG|Faiu*lXV#0W+I8}@kPbiP?rsH42pLSPS7 z7UGODE`}Ii;bCwx4-7E<6`HV)<9w;Y9PV|b}Sz#&KnH$KoS!@RX-mBr`ucn#&fHi(x*a1@*QRDSIcjx+yQ6f zQRgq2hG#4vs+3{Qx;fR}h4vnKONOwutvf2PhE5#WMzia>*LKVA7H{>ot?@c06g8{c zoEH#O`Ls{7talNB71!yFi^~IL4KGCv>&Y-rNX2cHf?{FZm{mYM!`!m=*bsdcEK3&^ z`h#{)!E0P%VzM#3uQa_*^`|>Qdwb`hQ4^7X5Uy#LhMjuzB~<|ySn561o1%1gtq+Bt zD;Q4n7Oc34w%A=qww`6M+lC615L5AdgPPyj5%sqPwoWM>Lsgo0|2_R2EAIuaaYGd2 z_LjLO;9&$iqp_G?Eo2otN1R`r@?Y5LOF?S+tHSexE%o0Ht*{r}N{X1{#24rOu-KKu zwZIxE&x?q2Cobk`7$q1G<%p-x6Ii6D5X~-r}Iz-ejtdwohsmOP4 zkvm$&BX8q%u)J!IGVyx9faI-^TDTfNfE4=HAzpWJp}6X?yft2bv>0Ks59nRVFRC?B zFCd(gzn9#1|A=?1Rkxc+8On#P@xpWLqHFz#x7r6djaEwGDjRotSWJd|D`37qFKjN` zgcfbPurezrb?S7%@3-LprIaROt|;S^MFkc|x!Tw~6B~?5x<9gVcLdWDygM{-{1LF1R?XRV zsJ{4e6S6?~X`bK$(kYNSxf@kPeAa2O1(#Be{R3cxDq;n%+4dqf*wlL3r^K#x*)u7J zb(YJOgZ1L>sGZ4wP^fJUedkR!B){3{-oae^l47Nd z)T@qZ(LbjsqpsVIHmiyBN#fzbBqS*PNcl8!rMumo(Efi-$Zbzg^k(i!KZA=b z^s6IfYE0}Lr3bnulz&;M06G)O@O1%7ziJgqVpmi5PEp ziarVHAt1iv55`gX7CBOPJE)8@PT$0<^fk?)47v*?+Elc{Mm!u?FyXg2tx6O!uFAFO zP?rvoS0+|!QO%^%MAu~1RQ&3Fi`;op7 zGLi8wM2V17xgV|6&;chD_#iR*M);0k35|!sAq)g-c-dM);kn5^Oh2^fl~WXznZh&- zsaOuqud)UhGbAeFQ`Hso->vz!wuFD1PsY&u5pXB&_I8wg5uW&Oi*!sW67h5cg+5B8 z*sWB;Cf2k$;_b}stgle+eOBdVhGhasE@HKDrzMVIJHzTaqq>I%6^nt z<1Vr!nK}x&r^s0NS3MM>cM>!tT+M4FpC+=A!UnD6LXFt69m9k3g#qy@qxyw=-Et>s zvZ&=U&ac4yx8uCT2%7-7&13z7;n^$tcviZhRU@*-UO~THx}&jWkFb00oQWxiiE*Cd z=n*9H1gIn;MM_Zw=*T`8SyFu%0>o1ftF06S4NR$3*f*401T>zM)|5(4_|;+*))lpG zCv<*wm7R>%IjYuupd%;a?U`7u3coXRLMg_x_jwkXtG^&3;aKopd*P?*<hFNZBBpp+_4b?J`IOu@FJ038w9>0jI`JBD9AV?ydCZOmYLJXYLA z{W~aKYhL%*IjF@g;iy!kU~q#Fdw21)VoH%oK+v7iux=$QOFH&* zMr>Xn({bqXotr|4P|XUQ4+XO=)F(2;QrY<8Fi2W8|+H&8e!vSX!U8F2L3vz~T_-c0q17Z`N$PUbhN! z+~1-9V1{*%nJPNi$oQ;{Ovp@1iT(J8v*Zi_^n64XADY*~f8trFBec!jqeNKVHoD8D zdH1%4NMRG>$kP79COqRnTdSD*_vcz{5fohVy-4~JVVCFzAQI=gVlp(`H{g_3A2-7{ z)U9n=dKDxCC|7skZOMCy(cy_)M-?9mF`GIytOe70`Y8p)NVqk%}wBgsP_zUokGDKRc~oip_pjBeW<1! zh%LCXW#W-o;Bhfb3mR&|eZliu(vQOTN&oGUT(@!#4EoZu)Q1=G$)JqENj|$erAEOB zL#T`k`$CYlQ)yk`oTWe}TeM)Qabm3pJ!}DKop2y3QDp|w=#ERFuv~`d-d1pbv}N2! zY4)@}jrqlidxZkofWyC?N*0+~4gCQ@{G;-HCH7uNB*MO?{qN3NX&A)!fv~CUHn^QT^8RDSR*8O6+qSouxc7PzMqg7oEW?0c`fDX0l)4yH!9vPDO52|^!dzyiZ%*|L&i>HaBZcz zP4bH_e}KF*-H@KTP9g`W+hE<1fygfkDeI|7S&RNL*^0FW?8r$bJF#mA zqj@UhT`R9r*abFkMqB-T@80$tas5&K+p*c4vs9dU25*!yXo9r7!QGbSye<*8x8<-^~i@Mwd9T2?2A^fi2wKn&u{yF07e0bzrX(* zVFId3;CS>)nD{UP0Q`Rc_Zy)964QQHg8v1VVEt-pWAEI|Utu9LTVs0^aumFtp)(l> zf%bt(5weuR?{%rGFM(Cdmd6;cp+7sP9xtNQnsR%dwzqp6(v{n%|8zUNPU*gQy&YcR zy!yzb`+YN`*F}!0RHJCIYfUNBm!ca*ipwRW%9%<|uE>31r72gnEY#>cBu|g21J z?M|+gOV}`Vkp3_QoN}Q@WDk-K#NKlRa45dF#7CKq7bd}R-#o{z?H)I0*Vjv}iiGZR zX##nbXQOqk5tTj)L6j_aZQThyt*cdQ4lw6?lKs&fJI4Z5V#=L{Qx!x|vm!gy)&U6> z73@kY)UZTB*~yOCsdEM_q*}`u6F{Ks)R9-tn7en*2$ucH4 zyebxoAbR&9BjI6xs}(I!q#eCa7_=~S&7jg4l0GT~z}=gNi|;X|gy?o4KkObTbI)bW zKs+NYOJs?Ku{X(VC^xXCJ-KX>W-ws);?C~saA!6Qn&*CTb@;=IA-nTz=+>@sr{Z4% ziMA01GS{cu-{=H=gdDzFs!m?JH?{YE=gr{}rqhKZCsumM=;h%!40h$@z>1mM#fh&| zKgq71hJLu1USE$Ow3p|$sR6ZjzZdyT2*ae(oO~8^|96x zTWL{%KBuqCS8xs`Ls|5fpAHbYDF6kfDj}zrFA%#t+135J@y}hbx%Y^5c+uf#_e>Fa z9Q^mfr}J#B9b48LmgGWgA%`Nhx3~MN+)R?ZH=%!ms`KKg(kOz8T2$caB;N=(1CMI* z6|qRYM#Wo0yk-Q+qiXLr0>R(=Agg<@GyVFQQs{W0_OF)i^1wT?14hwRY0$rLjev8W zv6mHXsEMb5hg}Kx{y8&f2P|1J(s~NsVP$#(cRLA}hw?uP%kvo&P z!{VV7oeC5@@mLEfgY`-gn1m!-3wgyA+)?PU!Wl-BMAv2PRDiD%uPDVVnId5ZGPNR* zm;r^o7Fl&3upZg6y?z3Y3q*;w%&*W-><;FK&ziJ*P4z#-zSQ^w%nIRZw6DD_sVpHF zQE75IktO_@R92fVA#Jq=niJh2em&sOv!~bn@yzhKqer?7lSC*zcVNN{avsgdnveq1 z)b!{h73-$W!j80l0V?QqRXFkmKHK-6YkSH^!3o9UUpE&~I17O=E4tER!___;5Y z-B z#8J$&+ty$QyMPFuz;*lZ=NKhSMADp0NG5Gm41BWRl<@bPHs3TD{p=paj zfJmy)lD%ws(agYmQ99LZ!w67JKxm>4ILy{k8Y!+S;@(G}Ti^p*2S7)z!~!ZVZ^DvG(QIQ(48tua z3g<9;m@}GwUhg&Ezb#zZy~m~vU8p~IRAgCg}Av{(ck=d3GP zfkyOCfpcxboY+K6=TpZm*mZ&kqVq!qLQNNHqYU#O8`d0|*?O=AIe+5aAVCckStXM5 zhgd*b+!57P45h{>#>~?n?h_+9FTM(TS+vRwgrUUZapjXnLz+?yZQVcOw9maVf2Q^0 zR4h+{a<_xcC4kZsiVxn0v*x4vmpeGzvlw5L%2F&x zf#JHh0c$rhHJ~^b_g^{68vCX}DkCQI8arm79ch=bDzOq;r2`Ue0@KIs9UQ>;_+^Ko zXsL{uA+H(PsCkM-rkyub5FBU~9K}!!(y8Gf(J?2?@O(}4O^agLuQ81T#cf4JxYQ&Y z=<-`2w)))wTrU`h3+VaSzWzmk?I9@!_Ts z7sCvvMORiU7GJ{6F-Y9l@*SEuP*D+KOM{HfWosY(^?MEE;o@T9yu{;I#nF-3+C7~; z;}ZC&B{6v{qz#6huYyaA^vj-*UO8;IY1(Ws~;(lgS0D|oP+kR>VWH2mu4J2e!$ zK=h#LtA*B0+~U%}-l}>c*Bkc{rSEe(GAV&O6hRMQ@jYctzD6N2A+XIo&Jf%PvFBl0 zx;!yN5ie{^%{Rrfflkkt8)zdiUZG*;C4L3P2G9uxA85@3k&UaZY4Yp!-QI5ueYO+Q zftb=Udcx0{S9P~N%w-TUu);T(C`CP+ZhFh*0u9(huek!=01FdFv^@Lz-h@59YM;Fm ztzv4Kq6h5A9|W7ReH_&`)|)cg0Q0H5m2v7H&Ab3tY60wf4P+|lX;$&Q!R$b`-GC%# z*UsdPIk_DX&ZGGqG`C=|!@fw-V@(v`mKOqN`&~%-=!(GrJs@B$>LFg$H>pm$&$6y6 zXl!!NXl3gU0{P|i!LleWOypqUIeYPfA15P1-!VD|YR`in)oTlw0FNyQR;%?G#5 zT%p9CKkb=@EXjN&RtqX58XJ}f9*?#n#wxqdVc?k?(tl_NctN{c>n?lx8E%uV{pnC2 z7p4eo&Q+e3eBWwh87gNz0VuoQnYr_SyXPgS1d(R#eIkpu?@lZ^4jqBlsHBOGWDc~Q z*W`-epJ=S=gV+d<1-9D>c@x(f?YNi2A@hj3Ni4Ye7di)tlb~Rsvfzt8NF*0A%E4+D z*J&lo&K;8Zpb2i0QQ63J7NkiWrXLs6kikv;=?$gFQjp`tj`p{`R=mL-^L9!a{+vLo z@lUkr$Ed_pTqOu)syuj=F-@T-5D+b>Y$% z$e4ovna7|oAvXA+zyf<}PV;9!pXejdCnC^?KD2Uwu`)`$afjAxh+OV?YTuZDir)_F zh3EbHSHwy$m6(QDgnbQgcox5PYIw#w0V@YW@@JTZ4dBV$XSOkfHH#G3DZG@RDxzvp zNq?@&7T9RYW|5P2D1>w9rC~#DwK$E`pxF%C(169fuyf;Zj^4?@v5>CW!3 z^ns8%Z3X0%7=u$fX*28~ij)4<>sVZ}Mkv6d0vL z;>%iNZ;=fwIrluT1q1NBpO^o~f`UBS6ucrd$0Hc^IaBo)bEdp|W%Yua=$M?MWz|ny zvhy7^4_1;+bh?MISt-+=Ig8R1Ro3S6t1VR<#vj+>rH!p^ppwf&^`y7E6vghR_IZZO zoOk_Z6R$5Iee&T2`P+ahz(M@T|6C93!wWVH-e1+@i2g4=;T>KlW@gF%o<+lZULjvJj?>7QtNh;+W5j=6S*{x|A zkxsIF0VKd#m5=TPi>=wsr_-9E;-+_Z>H{=OV5ul2(Czd%!~6u(`RM@SC#Q?k6VRSO z!|4rYia;SlYRL$=QAY!A03b+#6cWB#0Z9cWZyhQQ$qa_wAZ?1vFTo5_zP-tPh;YVq z=q!RXpQ#3u6MOC}Psai;K()>&A{?W{pbmkWPB$4;eUwbt`e{%aEL5Ax>`u0#d8igH z2jYImga?Juw|I@Oy%T{r#kNUAIyWQuhWyMkpU zJ4mr!mw6hzPLVTMhUh{9qjH#sT!@ANGl$}u=r0x0eJEVLL5=Qp>g5uG;**1q7d$&o z`GUhVDN;foNgg?o%MClx(E=@cCWEMB8Z$S+@S4PIbRAh_`7_#B_aSlbyysAEprrudQrwF^xW$QiDE-!a!k z7p5t+_Hb`;+XrB96a0-UTQ01%e-NPhT4|qlRIXElg&s)Q_)n%}r{EjiP}=1A8S)1V z{RsQv-mlh!1>Tz0h^ezJYY36F&w>$mV;`!Vz9moY)+mi{FybNVCV0g+VclU2lSyU5 zdXO41WFzU`mx>LNDN<)b9a|0031@yJ5^wu$u8vHKUgPAlk;n~z7^KnyZ7qv5c<;Ml zk8ZyCNEdB-=C=Rk2EhRdO9lyDKN=!2JL9R_rZAFtoJJ^c+q8R&|2pew4|rof*wIy4 z;Xs%W)D(?NwPb1WQ#O6pD9(J-HTG7)UnMU$E4SO4M@9Qz#X|eK z0H>>`&&T1CX!UAFw@+QGoL+9eU)r~;dOjU`wf|5BnNW%NRVa1yaJt7d-ePDu&=PO3 zc(o1>l_b$KO(y}bxp8=kX?YHQ@^B|Ank{1%u?};;cN|ckbe3Tz?&H>iBm*zMC|Zjy*PWleD36cuD4yiWn~1s?tcsmze$|l zt^{d5bWj{k)CZ zpoXg7MV5JjHvkak+&P(}qDuDl^WVJ9Z~<;qEC>Jq0#pD1uK%f!`M*zP8B1rE|4im? zjo%_8F{E$1z8}K_Uk<8N%W*z^XH{(x$19bPLyS~!(ags%*7n2}O-GNdVieuW-+ZD* zlIG-W0&o=AXbr8IshLUUSs0%$Z$J3lK@+b@*bqpmtpm>f7)jh;N zLu(?sdg?D{1E*9G9v_N9F|i>aK6B&59kEv_lDD06Mj53q$tGiYV}x^3Ei`KuclZpq z!@}kBzP?qt5(c(z19r@rN$kGmvqHF$Y=Rs#Wq)S5c7za|fvuE;NT^=vM28S>axwxq zk#wAFKN=dD>LM$yQD4K}S`H0bDw3%Fj?Qj>%8&}`otRlV1T6b;kQ!vAez}Mlnz8|5 zsWSKI7EhJtV`4U@`b=MOL3H9La6DAr=Vw|&ol0s_oBpoG`c)t$0a9!sMIG4R4Z+pb ze8zLpYD_;i%&c@}1ahp~5TvNdedC<~`-+qUgGrBk+T+qP}nwr$(C zZP(P>9WxQ#U#5S<-jPq-vT3jj_EATSImO zMt*e+nj>Dxyg}GW&Kxy|Lg--Om?PEM)yne&;S^jWqEayC5x#HF+}4dAvv5iY}p6vO)G-mdJLNoj(`US6Ms{QFKKLaZ`7QinBy9IV7)t zh7`6d*#>d{^rNO^RgWPWEuL`*%P&R1R1H<(uMWM}rC`(CQ09gvqK4?QcbP*Uw`gix zAnecbEamRLe$h{5fb*{#2k24y{p(xqr}2fRcguA(&_xZbnsKr(zdj57a$(SGC3LQb z%sqJKD{AL4WNK{eYz)fP0A3d3FOOqIHNKzj^!YXekGb&CIwL$Rkq3?V9 z8y9E>`C6j>?m>X)87p3^a&j7>+?027hs*mq3s2v#iYRo{QG{ZcwIQ3;DhPx)VMoV2 z$tw^F+0AgXuhdvNBC$BhVO*jfMMP#I(PRT!u7SKs;ZLxj`e&%tASYm^&hqDMOiz`= zk%r$}Mi)BBa1FihSJ#dla&!#BgN-okjt!KP%c3`NP^YX!zQg>OB<0x)T6Vm*>Y-8+6H?OBP@akpZvVuM2NxVd!%X7 zlM`%tLy@lHB_AVoEH^bV71V5qmI5L7{%*2;Br{5d8oSS+##l{9Yst9sNJ?LXEm>l< zjUn1XP#7_1Cdut+p0esX%Wl^VxPtNbWFtn7*Gi5?6e~Oz^F-a5w$PeN8eN!Mfb2Z5 zqUEf08DB~Zc_sgtZxt)>hG?C52bNnTd_*~+|&nB@v96r?0k zzx(FottvwzZ5>R#+NKUC#a8m9d66tvBIea7T|yef(EEolG|EvJ0sggMNx{t)qFz{J zsL`+cy_zF3 z(H$U9PnO47!Q&+hr5Fxu%2WABi}G1k!4l3V2XMq$5uGR9a7>+j9Lr?Ey)ilk6;Rs$mXkcA5r^=|rMa z6+L*5MH#-w>ki)uA-Zd)Y+v0Zv3Nuinw|<% zEe`lU{#?1gKV~J@or0Edyb3o)I>;VXXx~dg+L91rqfWr*>uZxIxe3E{YFY&Bca~KecP|6Hcy3=- zf_H;fZVpB~MoAA|OLHsHeUX*vs#(eGaU_VLld=AkE}3>>9u}Dn>JXcFIZl`vf#^1Iwz~nkH%SLQ1eu+n33=xT zpOD^BdrzuUwnWve8^&z>bRv!F&lT;rj9)}iD6a=_uEQpKj=XL-O&0LhF*B^kK!~~J zcsN#^)*YpI*L?^6ws_bH+g`r7NeX=)OAt%gGO%zrF3b?2+h^+>+m@@KA#DLoyw+^D zU$l&OuebEzMNVeGJWe~>QsgKCgp@@elmnNl9>UdZ-kM&PDLq-~Ly}u~0T5yYc%D z%>56b|9|Z`{=Y7xNm;`IlMUJXCGuwg^X;u`Ws0#7kC&vx^B@PY9i}%Pxoz6YNa^gAk>Hyp#(4bkGq# zG<|4NB6qW3Q7-9iHjS+Wqd_Ymm%;K`gd6EwW(jHwyW~ByUZXyu;xG%55pcTCSkN(q zDhSkn=gO9;-Y%-8**obnCW`QI8|wZls6cZRnk4V$@{ky^Ga^Uy{bLNdh@DLIbL*2( z7t(@sXZ;JLOQ3whg2Z$y4X9;?os<{K0yk)C zouR--RZYjHyrP~|A|$0+7wXsKx=y}m%j2Kh|2#*fWe*k~;E-W}N3_28pwKVzCeY8u z{MGwLI!{3xIP}=3oBT1>^z~yBMj(0R*#(;E`TRWQH_8O33re(|Az<^2pJ;CIo4?gV@9OA|%Jp#q!+S4~ z`=M;V$ZO1^;66$T;38W`I6wfHm_I5}`yS z0BcNtvd(7y;6NH{o6_LleLdVP$GLL{*w(?cgztu3Pri#XA{xc8uJ&p)%CniJPsQMA zL!QZI_sgq7i_R7HuT|LJAqXU?{ToliP#-H;zoJq1*4Z~g1tBvtM==CAxjvA`4jVhm zQGKLF8rJ*lbF<7jWx}WxU#wUNdDvWfB&352BZ9AiLX_~~L=bK{kX`XuVX9s&<`@Sx zdma_nHati$h)umP8Nb2p=8Ql+b}yp%cbydiT;xb7e_6HtatIO~UbD9v(?&q!Eqf#} zjRt?^a*o+?ApfKHdWvI8JA)_?jU& zkHiXbg?TxV2a;fN2sEKHS2`TM>v+1jMV)t-OflInue-1d(^q)Mw8O|V^d@s(Exaei zanrsx!T8eMwB@Dfx3={L7DKS0FTfT#aMIatA@+ud?XeD{&V%^q=nG!7tFPtUzF^)F z#-L{^O!>qyBS?4U1D8=vs|Grn@ZDJfz7R-tG790|Niv@c0()M-SLFw7=Wp6>f&Hgb zn|K;4y#v`K>?pl*yp9^@05klhR(7?A`X7)zNT;yqG>WS{L6+SNo~}d?e2V`lYM8IV z*M|GTpQ`!dn7^q$$OC;%pvaIG|T-b=ESD|0;VMI}L z7?Nz7WIL+VN=I%cCvPQ1s)h?MU>0>A+RMVR%)M<$AD$V;T(1nHj(hv^l=%MmrzUvM*v9{kmX%3gXsIXn&jmLD=Kss2%I&r2<81O z#sC7V<#7FSjxqsk%c_C60E)*{^TPKgORBXMx@&g(=t?-SChgT3BPv+s1l*#gGBtcL zfb6MOV8ITdhhJK=pDr;E_9c~*){C< z7u5fBxD1>={AvD8=aGW?|IH!)Z->i&#)Z;0hQB%F%6gV&hI)=R|E+OpQr?gz5{35~ zj{nIk>|7mQ1|lSgaIUIua=73RVTZ4{xi~DEdZ8-K$Xn>yL;^`&=xJ^;IHhpRQ-d+t3IM#4j$!nw z#sBZc?VAL1!s_hL6{7({1K>Kp6nk~QEZl07^emHDsH1{A=uLcG83$;Y=P5?hyh|pO z_874rSzyr67@^rKx*^OC*xpG;2)z4%=<7CCl>5MKoy4+r@fm8_0x#-JMB%Jr7Q(pE zpPi6bcb0P>(wyz1U1xY+dz6&`TUQp`fu}8SciSDTpn&fIE1N$ra0MS7cS->CBGhNo zVLU=_>DLe-Sr+;%B4md8Y{cC16EuKQ`l(?g@)tyfdy!q>^Ori#EOX}PRAePEZCbHN zvg<6mMymSE+cxi42bS*JzJB5UpqXE=1ogAKya)(S>}Y3aW)*-b>zvJ>>%gW?>sQbJ zJ{q4OcJ($jQe3o9X;JLx>GVa@9WmSZmW_Xdq?x|CXI>>bT6oVgou~Ii7WgS@e(DOu zHO_%dNk%O*=i3LC7OjVmcPz(JB819KTEk0T&DCV$Sed10$4Xxqh6M;X3sf5QQ9_wnI7zZYwLUTO z(5Ph~w~pQbqw>D*viJK|p|6D7K-T+8;^RDbzmlKPgL#Gg(()Cl==g0!-B@}(9D?T> zf|j7&I$q-%Bbd?7q#1&EY>K|p*$aE9FZ}15JS#Pmin{Er5{V!D#9#&X)IBu|`$IYK zi#$KT_3@h>TW#Iy*(k!H8?lSIQ<+nQ{AeDo0%i)1ZU%Sd1oE5;n)6ef$F*@rf{)h5mAbj?Q~T}1Ch1kl=cqaX- zgC5OXHKoaI^0i(zt*L^C%>Ef@i^nv({Ps4GaIsZxZOfq1Id9xmh9^j1_3VUyWBVjm z3BC>KN>f~W2LOmuFnuZ#vc{vt1EjwB2koK|G$E-9a8_A}$2z)~gUOA9md{~&-7$_> ze&|n8jXMZ^zQz&B{G&AM5o>~+KDHDmtgRc1a00xMz-Pvaw13PA33LVDU* zU_y$|3!W>C<_3U;m$#wiueEnb&vLF{Z(jGNSE&Uy6RtZZ_I%<#)5pB(Zt9zB#{J>k zKd}C1G3+cN68rdNcjtbMtpDepS^l>a{?}wK3&SE|qxru?R;(*d+l}w>`|WG zTvR+cHpFeXGNgF;i1F|WfsnHCOVK|bE@=1=rwCkiCYJH5FCKZKJKX&}CNX$kz%hE< zDYp- zl+vKwkw8w1nEhjE(BE>B7#Tyv2h@=%(MAP(C990a6piI`)=B=$#adfWs0)ZE(M53= zSEPWEy-_r|h>REJEPg7vFHYAYGIfkXtb)_Z&DZ1WQ_(c8R~Ug3G3Oh7QmUOtDw0ZL z#>pb@zqDXZ6nWXFT!W89BA!L1fx)5{CqM~rdoOh}^Zg z-4nnSR?KKiCBZBN2v_khk5QyYA&r_Qy|+KUd!1w5xZY?I3(@#6$PY^jmE_I?A0?aX z+ro^x?TE3fl*2t;YZeqyE)Esci@y{H*$WB;b89j6OwOQ@^CVZClt)yhe&pX?6eVP; zn5r6w7K&D}-|Ve7z4zDi7W(@O-maGq`{hmy-y?i`x&?RAsadh`U4AUHxrvaPm7FK( z-Lbpt6Qe?LO$FliS<@v=WkHl?mVPkDHerDX>|=eJxu>+j-AN9JNSeti%lN2;G zv=TI#nI2$QwsW@gt;O&PM624{My4pYbfq0O-NzID>06hPJ36$lH3wG`@wkXkJNviG&WFdhhJ~J-pR3Nd_BG3SmM}w$ zuz5+k)7Q&6Hi8;|DD?KB5=h*FX-MY91)M3 z-o4!X{i3NnrkI6HY(2VgjB}kSw6i6v9`EKZy1cl>vx6Go0>gJm)uJAtP$iE^cR2^! z=(jT3-`eKVG^ZsU29XRAiMxWZ%b7El*I)aCmj_H8RbA_(3)kx6B3vrmkTjh; z0r=Zgf;(W*CO;D3zmhm|no-)?i)UIA@hnOEL;mrgjRI{^^Q@9e~NfrW}!KDcgyxXoB#M)b6uy;jhkxMRfI7rw0`e8Y7PXYRsh9F1H ztaVi7m0E~=e4#Y*upqHsEU@c#Hwhp~3N`{N83}>yI%utB?luBZB)R=xQ&^*V91@|( zC1L=3j!3kD8Wv}Bje+4YwDm0D@rNhJO~7K~{cfL&!?V@{#lzn~P5KwDNl%N|V4AeZ z!=pw*NC=;^EA|Hgv<(a;nyZ|6x+CG-e;1WH-bNxW1MR8JWYPc?-w|^!6)TC9=}>5a zd?Wv$y?xxXm0dU9r)WvmD7hxoc*wd(`DLHDL>F&dsf?0&v8& z=z67t;mek>szrfocr%q%Z~RxYF}rr%I|?lwl5EWm)z6ZI!#=Ia9u|=>B>u=Q{3YR( zE0a8f9`X5fcY5j`Fv>`2Y^x}wrx~0~KvWxcW2b7)eqXbG6&j+rr4J%@upyj3yUzGm zJ<+4gQjcY3N7wh%I9W(En+yOQ7pd(T_yGtN%z#v!`>=iq}O_u-v!L_#(<`^iw>1k=bQgu%f3q=S*dW&h?O z_drqYFTLBmh7r;4QJk}3TDa8do1BzhMQ^3{En93X4&gBFvhsycINi|DFt@C1AGfc+ zEi^u6SQMU_$XiA<3wN=|d^+Z{Rrft@w((}2SZ7HZSUM^0?yJd#ph@~WpJY=i6kauk+a?FSW#jQV}yFn3CjF1T~Y9mB)+DgVx#)M#!HMNzL32u!Y!! zADUG}R%SKP?w zvq;ez@EfrWPP2cNl0lyqBT?INnf>GK-5riJyA}q%>pUzwE$~Qk%eO{Q!$B-3g+Z_c zOH^XTbC`B|Ri2)=V4{7x%0k65t|TX1 z{7pm(bHEJ)ArG?|CTn9ae-aaDs`(Uf=zx*c(L%1Qj_$KQWO{|e2aVRzoYF-gcC9|J zLZrjd8W*-IwAtFa!x_7E5omn4dFF%bP0;MvCyBxd;=(7@U@Fq2B(O>)Do0xnKzN_WLp5?DW0`z+u zA4&O|#l9r8B*nt=vZ%T>?n4LZl$WB^v#k1Jccq%HXs(E#HPF9A(){T|g0KmWS5o z1Ym&F3MZSVk_~dh*qZp@#F;I6YnGKa8ti;y%a+}~s>Z`q8&V6&ib?3uj}G(9%;pm| z`8`-5m%uG;0)i??h&DeR8xnpXi_LZ}d9lCMX-pyowLVknVJiv3l_S4zXkhPsasAPu zk@lbEBfDzC4~?YIa~v~AxyU4%#lVruc~gk%9ZUH4>GQzC0$!czJRWGeme;ON@IV}RgFbCTjHcYHbN)qx@rCzTx@;EGa=BWY0^5ECrO$>r zhj~C1YH|pj?s-Jz78i5Pvr}6#$d@Jggp=3BVAPxso~#tDFhbB(%b%>}c;X3ZErk5N z8G&odA!^oV^?1ECtG6L2%1Z?3<)3#lJm>a~Q@L*>9l$~dJ+GSJ-nXfH4wnmeTaChb z#>*>dM<59U3p%-4)@0aH%^jlF1mlc-(|(`t{}pT?g_mEr^BcP(!3_Yw^nV#W|G(jw zqW{V9`LAAj>c6G14U7ptwsyrd=nJz0b_pVKnCZzl#*dhC*5_lcjH3Hu+R#Acu*g%y z-#1%Z9=osr6clD$N?Z&OmseLGP9rgV@95txKWig+bu+6|g0(j`!okCJf@T`FBzF3zEr$4@!jV8}-YF~BHuXc++KrzeMalSG~Z2@<5Et<`C40wknV zuR;hcPXTYf>&>?3)1zar@%SMz~Ct zM)WRkYi{?lq0m_v_|Xc0=5#0(oldxF^rsZUva zQrRrW7J&If&+%dF=mLwi)pfudT~h~+)_NNo*s*DJuL?j4iMA09qSUV=-ed`MlnnW# zWM$w+cWU?T)Y9<*w9S>Hy^S3wW^H1^f>l=~;38x0_GxW5U8}3RyB#}cI5H}zc7NIb z3b7681v-}2A=aFbl2$E9_(LO!i5+;OGOO0hnk= zNQew<4z6e4>cw4e-OD^9EmLgY{BHK~Zpp$q6cIc6%KSo zk#D|g--PkM%vJaD_RpT+I^CUmGuk{3bRpaa5OA!~(b+hU+1_uLV>)oWe6}(x7fzlL z#1s;8+*2v9?mq`Hki1?aCqU?GtbX3riqHZ^9CU7V?8B~L!6_*y9~k+n(EMhfYjqy2 zAb>I_y#h_Dyx^=;1k7O`)B^9<5F(a?i273>9+kG2dWkEDxW0YI5^_lyDxu;dF8$>= zSU~AwNl|04smtCL1yg#TK}ns?)Z?H8#sIqeUL^HGd3DB}%rQPyVR^B@5~yzSCvd{s zx0#lBgmX}ScZ$Y_6_@Tw<~#go(vwtS+RgB^BhX?FI@<=l*q7txG_1US%TXe*$I- z&ZWdRft(*x08CpDMFQlJwhRPisMWdSi@dsNIn2{Z%vVoRYmNO1!$E)<0e{Hr;+Ka2R`{dJ#DXG#ogoSb*FFp73`(Nho!ZdsU1UGLnB9?`4Ml-r|H{7_z*4q zkvnt!*ULevGXSRx{s(sy?M`uAqjD?L1Ua^`9n8`92@bI<`1w9Ealj7 z10S4qD!LP*Nu`nLJ_;wZBOnWh3ORCLJtzR3GgKfd?+vh4S#$&s^fv`w1d3Aq?FSd^ zWvg&H3#ZMYY5$50ZXb$el{O&{E=cOlFh_+R!QQ-mYWlAB$;!C+4}da! z3vZ=08*37XKWc#4wYZrc6rhg)tMgObGp^@0fUMz3q}GNAsJ!O2B0nT)#0I>e%v*J( zBB@o0z5S6=e!b$2IzmpKfbupRhAl~qQGtZ>a~gSr06j_07~K~ZV2Wnlpgkg5)2-#Z z1q_d0znNVmFhmC8o?3=Dd;k+}u>FCdjGDG-U)I!Ux<~%uIhNle^k&t=76jo;p~O8o zHG9g~tU2k4ws30gk8%Uoy*eiVSu6hKboX7|J4v{ol|c;n<#`sl-dUOIcFf(LET0hJ zr8$<>?`Q8%Yh^9FeEfz#*QZ+#X`wW%vM~{E+ZUKj0DsEn{TvNLj){O_=LqlNz4Hwq z=YXTdeLxIl5Z`koULpLGi;+l%W1Br^yt7}Cw;)%c~4Y7aw~MF-qS@FGjnjZ6yT`@QBq4a=o|?pP`*cl zKi9z8>0PFu!AZSQH5V#?3>ih|tNqMZn&RL}Ir(KiR(EEr0cQ{vV;aZh+bhMkJ zn4;`d1`Z1aZwLOeqD4}O2xADG4DTnq1Rg?b7NhXIbuk-7N-Y+lOgurgL!=^>kSeeY zmmd_|pJo^|i9r_?B~n{VqOkKzD|+54_Lv!lOwp9NN!Q$$X}1x*{*Ttb&C{EcF4${#1$3{QG?{`3HFilJjtnnSCLh8m7obNx<-QpDiQ$#UAbn zFDk{x3dssH97+ZK73byw21|4YfUsK{pp5*6|I)XtTMxAXoC+TlDSV1?LmOtKLXVqsEZz46fEkI*a)Hb zU`EBd1Of2KZK`!hRuPL z$MFU|N)@)r@0>+Al~Rj@X(&c@mjx+&!d*NuX;?8Ovax(8dx*pD;+Yp8Z^R;q6;~vB zs}Hn*UA$(B-8B2CNbJ(W$C1p~@o?-huiOVH1G9c}7u1#lB**ceOkLbCFg&$H(nI^3JJKIxr8sDZ=CG-^>?DE*+YQ=@@d znZ(=KJG66sPK{lIdV>c%M*{Z;l(bei>~pKyhhX$aH#gmKoHx^P z?e-U3yy-iqGZDys5@Pqy7|ZUwY>^I9+m(`A(#+8({T^nuE6HDR5!7%ElIiK zUoOZ((ZkGTJsTe37TP2t8>-p(z$?F~rsZ1g=c$6Fbda!-XwNKQGb0q<83H}dyRu|W zO+L2K4pqF!1dqpMY{KPw5(kGOb7LEWo8JtF`x1J!Ih_6{n4oHpBafO#^!!ZBQS1z+H6tjL%7R<9Qp-V*^Q8?|zW{;RTv$DEs96v_y5hIrJI9b{+ z1Py*6Doyflri0z2kWQ2DiB~bPp%t9W%32=GHiaQ)fsNWSc#@{+7Zb5#lbtZP2?B4<>rOVVSn(Z zR;0r%$CFelW>bAjigw39)p<=-P#{uassFNKuX}yz`MUT~j%u;#(&qVjIrK-rU(P|V zd~D8k;d9H01r5KECtA|`orRvm`N4v__rva}hnP-TlzmP$LZqb$3cj$9Lmy~NI!c9# z7%Cw2iEF%*G%HWkp=TT$K?HfrhPWv}$r542Hm~OH+|*W2m(Dsr|D;^Dd+14!xEP%o zN93=qRTF)6FGtLQvxzxEKg{q_dH@;;Fxn6CuvbRGa6Oi`Jz{3(n_o0ggy*j>Z3Jq< zv6M)LIX(vl=i~}}N0XwB(S#@iT9(><1S?^6)}>-0yQ-K{_{^z*G%VQ@lApYnq(H+_ zqGXd~4nMS7$3lZfj)Ln==Y^sAwEer~MAUPFhs>!D;fdDW@t=J|+^>Xk{MN&(RHZ=h z$Fwro#vNF8M~3#hu}H`8T7Xl_^Gl_bzgV&v=w$*AnE9_We0)mn++oL`1GypbGP}FOqb`Pq82b2h0-9(J>zXknrmH0x z&lSK*`ILWQK0b!>fJ!pB{=o3ojoNC$AU?BgyJhDaju3lyKon>NN}5^j8BMm8)o@K} z_Sn9h*PFGA4=RPZT>Rw5fwc-k1Y{~cn1)CbZh-N6vEI0pal4rY_dQVODTs7~z2zjeR%VcB}{N)MrkCJSE zY^sK9+f2h>W#naGLM9R^|4`%XduWH|n2qwKQnHl~qU+lDvrf~Xs^USGbrm2gyV0_B zX&j$Vpf-v#(P54g#MdwOXd|3HH7GvkizM-;6h;v7^-l9haif0Hir2gaau~%rlDn9!bbJOvMw zq_JAoLXqhtFsX55dq7Cpz-9-L*0G6MI;51){50<&-2Qa2lnMxc+m?U3f4{%@T<{2L zWwvc5p~8;a$C85Pr*u%C5WUZ7xPNExL(O2=uZFX|r&Z=I{Wm>R`k$ZmwD$=hQ8kcV z4g%j*NAFcd&)$&MFUcZZWOsvo=XAokL;qt!Hr!e^TMwV@3y{X z$YemGVcPJFp zcmN%`%Gr$KxFRuy*Yl)IvV{F60i$mUtB0(X4KUMn(l8`s@nz~?G%q8N|HUjpQjSPE zji+mR1J|l=&@2O8DII!h0o4K4`eYl@fOPadwH;I;s z-0hDK@c&%9+DkHnSpA+V-u)tL%>So((SPPde{HO6ZLE!~{~L=)QrWP^VuSaB+x0;& zgi8W4q8W2KP!>ZFuEhcA7EmXm+;+5!U-zw>$ht^N8@L;b zK>FnIa=Hi77U(>@Wylt+WTJV}hg#5NhwTRn5Rw=}B5MTV35l_c5{*}zplKy>(AKGB z98=Nc;NGX7Q5rvu`ZtTg7MVzc{!N1xT(ZktnTAa97Y!O!e=4b^e)qj;N*43!mI!zP z2ZHIf{841DAV!+_b<3C=!NHSw1EI_Vi73k|N|hR?QN-%hrGw{{c$qM$EV&J?05K{h zH5-{}U^1C83v0UXylsPi0IYV2gIH2;v8;AFR888KU6+z)X;?$`OmVWsF%-h#z`d{Z4bUog2-`^SKQP4bYzsbuqk_4}jKADTroZg=Bcj9^l{AD+ zU?TlNtOoCl(Z~kB*(-N6-zMKMeW!>2%)pqTD;gh+>^cpL_mQfZO~m`}x#HwB{ZTV| zH=CUY2Zk4a*51e66uWmmb@lMw){!Hm$_o;D|9~xGjA#to00cL;H12gt3PNw<1L8Hn zCBrCH>xrpj5yDa;7inRYfNOue2DD;vt)WG9kqkSl$$1F=#`jFQcoizIgJO9fj&Ewf zd^IrEU!veEFIgL#l4>`wVahO0+i$SJOi&nHVu{*ey0l;cM8|#LywGR4$JZhXrs(ri2FG|G zGZ?WXO83?~kPeL@Qzu6SPgJrl@rvF0X{O_v`B$HnJDxg=wXR9!)=Ig(ubF`PFi zyK_|aK8c2%w0<4!ecv*$uwETKnFegoWz}`P^~CT_PNW8u53xd0gw1mJNM=lws7i_w zIeKSiECvO(9A?mC!WRAKJ1-l~M=a_L)Nd2G_HS#ksVHU>3d%p#9Y>63%x*W<7XyJ1 z_h*o-b)Bx_UjhJi-zW^dbPi7j${w;<1JGRXhZ;uTRDoCmw|bt#U)_Zm*s=61D-w+MaeAIiKuOH2(K}I z{my-SU1|^EdQl6Sl$KS$NY?=h_mr{6SE3Xwmssv3+sO*SMqr8BQ95y@~;QM+U)hi{8XQ9;{B6jDu2a!DSY5yH4zhx6%(YK%XLSUXZQxv#c?B96GOzo zBvM06kus$oNd$WVa6Jy$+ltNDCCAYGyXd=WNBxS2l|ZKP0)c4u=yV02kbYeVCDI0} z+!EAj(Pl^!x3vHAwb^O(vK8)1lz|wV#md5r$mKB%&M#RZiaI3YkD%gefz#L-_E{P$ zkJe?Fk@PmueT&XhI2OKMYuO+40eJWL#|aLxEaZp}%@pUxc(FU(AoX2L`=}RW7h|lT z8V{qwVc!1!Z8xz?t|fCx3j$Bsl99P78pDvJaB)v8g11V_E?bw-{ciAD zG09b|41)5bt~T&9DC-Or5d&6xyuAh#8+y^!MmuT!KUGIs`>4C(zM`#A^Y}kcsP8zg z93@M!`L{n5Zf7Z1{`%*vR376@?B8$yYuxV>Tq`&J%eMLA002<^U&^OJ?9}kn@ z!|QV+tqrtI4nLZ>hxf_Jzt_AT@6bOuovzQw+P$s>yCZkt!<-A^Gb{9zg(&zigR!PX zLi$D00yDcKub?T4mXY+jXmv(jZIg+r;fwq6zG`vO2Jfg;vuUFS@~p8DI#6j56sj<6 zb@2=6T7&c{Y=lNs4tY9h&_!}^YzFlG@wU)Ch3-lFqS4Z%tu~BtqZq$T8^SNsW{_3o zz@ejRiqo=JyK&Xma}<@?FIM3{KrTE2B`GuZ5BJ1k+%XvnP-Nzka#|Q~x!spon&G^R znKdQBAD|AtWC1v1G3?GF`BA)H9JXXym+4wzusV_EKc+9(fNYy=fKbtI_L@B zUP<55(yr%cUCg|5o}G$r(HiPggwR5&7H)}i)Lz4{{z+woFL!!(>(-L}TTHDJFM38s zgviy$4g|OrTQe$TWW^+aslV;X7*H`s9ZtMpiB~^UzED7 zkADdn^)9M+o(w3%Vf=4w6`rb6(E^7_v^YwuS1glQ(^W5J3Bthhb{X47nfbP#Ub+wt z`#V`A2n?oXIeQ;w2Z7gD`-Y#(x@!C%gw21I z@~>v(f*c;Efs5&b9rpnHquZxFEcT^US&EsTQGL>8WAZxA6oyW&VLF>(TSs?AYupht z?kG0w7TB2ftCinSnXiJ67wfim^{RX<$g#lCd?7lskKMHeYH9DV6CfltYpAc0bnt&h z95V-&b7b8E!wYR)60DpH(Tb|Lu-dJ=ptr1Z3U^;>+z97afIcGlC4>Bvf%*a2_IcPB zG=?Hxe{lk<6#_%2?&4uAC+;|k4Okz$r5UjG9-3UxloH1OKo^gaV_nifj@z27;9y-> zCvDqIx0Y$q7}n>;u?<1LDas>^$yOe#vUjL*b|9r8vM4%U=ev2lbw!vHCH)TZLAa<9 z(uy)UWWyZd^#266A`d!7=WvhII*%%_+l8P|z~jWa;`_`u6l1td;v$5^SJge8zyd1@ zZ=1p>r0U(c^ol{p$@H$f4PoahG#Nu5cxuarfHkg>#yPlEB1jgw9+gIAgYu$)Iy0q! z%=^%Y&!b#Zv)~k#O6tZC=!!j51mT~Xwa@&tmw8i7eY$9z~xp|9#%t0YTpi zdpT(Zm>OJ8=()QRkdnT-?I5>WmQ(RI1o~?&bRurAAg>ji`$n;fCAWjenCfjXTz`q4 zw(myjbRC0FDs`@a{5f?MnEO0~yj(1vOoh8}RA~Ee+P=%ss6C7wI47i!D8Bk(*$?UF zqM0$G>xhbG->`4lQ>*Q;HB5?4l##CQ88(}0qem=f#;S)=WHHJGVLEHlDm7e-$39FH zSVpbNGii*8VL+sZf4C?ALvHJPm>3SioxEPQT8M!pmY6A#fiuq0pfeguT~$0fAl84Gm3lxgufd7`sn=8&0)8&_?C6Sj2k z6?He8jXB9*L2q7FN@9HnhnTY?w2TLUsuwBlB<=D(-!!|HMI8>evhOgCpJjXqUy1fh zSPv}qb|l%Csj{p9`@80Ds1Lu~am$vchl_gb)ZWvV3}9$6c1K#+T+9JpKD6qzcSKnw z=-SvIP+Gs?-?!b?6qu)^DLyEmYR4C(yw;g7ATr65^vTGK zLUmLQ&QepPe=R!JMAT;rM2Hi|C3HLN@%X^{h1fn(wWurDu_XEY)2}D)x;K|97r9Ii z*Ti%&-L_^6E=jd@#62)5+}af$gVSssU;AL0e6HwCowx1Uup)cDKZ=&EQ5!`ouJemQV^6*7ESMAH=Ix3YVeGqoi=DP^9Ti^7af@rF5~1v?R*kyic~(XSu@o z0gss6u26z?%<*Mj-1RHMognX|+DbUY(b--y*zG37Ln}Jbm;G=EUJmN?nm%d;%&pI~ z7&7+Mm?(_5b-da-|0okRg$&BEg)_M`tiQY8Uag?)EVFv@DfwA;k^dSCe2xINxAyW} zLcn&&3gyQB^xppD{LdHdh`6Q3;;+iw>{n&}e+mKmB`S>mt0PKMwNb!gL-NVN=pG>T zG9Wvyo%)+GoTqRA-~Z^=8^VveP>BM>ecE}_OZM;qD4)^PuN58F{3|j4 z6E1RLPdWv=)IwN2Q&O-KYvRhSS zt~?r5$4iykF2Bm1@NrFrW09( z+<5++K6`X+>)o_|AoXrps;0TfVbT)6>mC@5KwdDj=1?-ex0D!PwsYNuY}cxv;l1$n z7xf3mH-DRe(lGetu9j-jvt#W|jBsRXW-De8k*f9oT4mnJww~`s5(e-(elC-NYYL<`DeHjguNIdGI?21{K=;df@m*GF5c}@QQHU9CNAmBSUmqG&@|A>Q$KvV)3fiL_vDOUrDbIWqX$ z!e52L_M}k(<3l8#Mwn7$gA3d&>vCA2f>X~vG~S^3>YEO@A~N7SRrddI_D)fjHCnoD z*tQ)Rwr$&HhHcxnZQHhOTNze{dE>8Lt!}G*UiLZbX+5pCx#no!7+>#;Yc+&M)JT-J zhV|2o#+9gqg-_hjvV<2nHDsJ$U+G3bQ~ zkW%ex`M;!Std;iKp?9`QQ#y~$&siS8KhC%BIYdYFq;+p+#h&VUnGvIZdow|caA}AM zp!{rlaA46rE|W|6qmGvrS_ixeR+g}^GtzD$H!sZ*TFKo|a!Qs*d<+X%7Lludd03`4 z0QBtDiKNR(pIV}E;N7pJ~p!NPrpx#m92r6uuv_rBE+>$q|9~ z@DudI$YMQtUZ+$eEgT@-g=KoN4VZ*s^_@{XQ~*ILJw=(L$RUodizoz=j!l@EVC!Ab zlcVBslUv#6n0ak`z)i3h=dtDDFiRt0TW13FxF4RHn6cY|&Pv0I&NlYvOe7Q?M7}L84EjEI(ESTU|sH?DY z>)D423&SufldUDi!%7Ot`e97Wb9ybZ#!s)5S4;x^Qq!maQH*sbu?2Bbw91 zPb&gL+&LmLub-$tud^iq;a#DVJAR=Wa%2~Ps6)nDqzx&y!sb|2E1>kMb({Wjae2;N zZXh77yX{afxy5{Y`7;=rCv1){*Rnmh;c5M;#0sJY@d+1}CtN&POEsygA~{Tyv8)}R z!)wlzqY_$$t5|}o`j8Vv2xrM*K5)N`al^aIsY*vGrIAl24!iqYcbBgR49P_1ID%Pm5CVX%~vIa zNk{#M&-^bF_s0?Q_NhtI87CByE(#AzE|vR%-;nu2e2Degzeg{dQ6I=3orGFYXX%8`!L| z)l;w@UuAAY#Dyh4z?ScEQZq}|e)3}#G!0fPG)HH6HviI{_>feT=d%6OVCR>ir)QDz z^X|a`BDm?LY^{(oa_c0c*whu$NL6#Bl=^(blb<~$+JdSX%!5Mi0MjFuN}5>V!rpeh z?bqSFz2Oo^KO^g0!a=_M=KJ5X0iNtCGdnN<00`KB$Ta_FHV`%V{Zq7b|JR71ycu)I z3gZnQ=Swh`Y7~EWwBS@aEydGs#2UFSjliWdRcN?a-w|;n=>YS4-j}X?%VQnwlmKH4 z#eK|CykfK6clD}|OI#`_)*Pt3Jc{uhEkV-*&y^&^Y#FM&(Gx-ie|w z-_#UPugRPlSIC*M&{NIe7c?iM%g{ccp-sigg!frK+s&XGJ5E2{J-MTJRD}?x+G-Pt zhvaot9Ssu=H4(egi>P{*fThJ)P)UX3Syysr2Iw7ulaH+xKYN-s8Vz0eBD-xp-Q$F99z~qO7G10Ryx>iB4^3 z?FngF%ub#SUoiMamXrR-^<%WHLb+ux zjSWR9nO(d%);5ce>?OI5E*<Ua>%Ie&j`uq{0OBz-n-8t{am zN<0*#-$m=$8F;bYh7_~Oz)`o=tj^dbV0#E#pr5NFUkn;g;!Na#4u8el=78s*#>bGmH2QTqaC?(S*9VrC_*LdOBivn+mNa(V6E%-{VtKE_yhWk>wa(&_4F z>GU7jhW>dBWen{8^$4n3J8rNeeC?!vgl4zXl?dU8)2{9?WS0*f2g8wK60DAk89>Wh zlBgx*=VX38Zzj`hA~;wJfmxY9&tEs%a=JSQ8 z4g3VE7LrC)$ssKu@z>B&75||lu1;}!*N-dq?Ek!4Lyc*XMQy1;lkKO)*ZofP9P{0Q zRQpqki45F80+ZJq&g50KvoroHti;5ykx1)DFLy!0&vqsn@lL!`r@o7?6H;_V5K2bX ztXe?}FaV$N?B@L-ZXxndmvl50rQy^o^ek65D}lauD(3(%OmP`y#^=v>2ul~PL^h~G zi_C(k?=x~JX%bp16Q+jN6b@~7>CviT3kfSo-5e*5;!rH!Pmv-a3J&#(k60^@6`3j) zfPyh-F2`gr~YHGo2zc%B47@YMoPy#;-}1I z<^f2NP}6PlLWCJT3kh+Sw~8T&Ztvhmwt#|$<1m=&zTu2P4x20bxAT3JLpYJ_1x2QG z-sp>&-@H?`ZIWRDF7jT4fZ3xoy$LkqhJaUBF4VOQStoj=@D_ci>9Hf1q28aGiFqWK$fdSlmU_ zBN(Y79`^GW1IdgjBz&R(-G6<0eK&efy8+#@M4CO>cIUn-IFyYU(J z7suhfkA)t`9|s>aTa?n|XXm~q^T?Y(5!DL;{mTe?J2U(DPYpXbjN?cxu6A}l>lfNx z-Q1sr&z`lq+gC38DbkEP1@x-e8@ycZ$@MSLTJ}yxj^q#P83>sZCdC>pcv)>Rq2;Ab z!Lw_lg82jBG)1I^>n3%*;TYyG9U33_A9iI)vOz-|Zyc}nl-R3s5?mL9;gT0-z!dWZLH&eXvR6FpL^^d2HA3k8bq~!gL9q}PoCEK+T+j6-2TkI zH(YUE;I30ucQmIk;s>u^IE#7fWJB1ZA1}@bW!s^z1Pccg|41^j_9Qy0aHlBZpKBqA2ur@DbM-%r{Ul*;sl>PtxD*g^(Gm zsb@rtvaD(z&KS%Z@}mSphp5n*Tia7&h{&{{+`vB&qmgSEY}ub?L0;H!JX&x%*AmBQ zt(1?WYUGB-Gyc{Y+eGT?OWX@VXXg$2fYbhvNi?TKH(4>tQbCK!!HDG+ITiO1y}}02 z_&gEh3r*dFcbuhk_uhH!jWL(@c~5lC`srRs&N}x=PCtr#cg{`UP6ZBgTfKJi7vR~n zYhTi6+_09Q=-4-kgp)`{LUThfms{##GQ~^k(vy#4G#s<5UYeyTs{y_FJIDAVNrPH@a}+RlyV| z_d;RdHvEME@i(V##^UPSE1j@HcGPg*YQ*fd7r1fT$O8(fmSi!$1k6~+*~C(6`Y^~9ctA$61cMJJRzb4s5N)`t079puw<5nWZpG)D?B+hGT=(ZdUyXqz zNw9c{GbrEvE0fVAD72eDAZ{FnF%rvT@Qh98+F~7OYWaiKx_*6Nj|){c#Gq6{7*qT) z_A)@aG5&Pg`pu#iIO0U9(~G0K%_sX6QzC~O3*3xMo2`4zrF#hxO*_xjAqG;t39=X5 zI=u;q{uBq(IVxK2$S)BI?|LZ5m8W;cUrM~e9z6XcEQ7VTtq$Z9tM$F}h>vd3Lt`d)^0*LU-bR-QMG;LC~ zsVw5ZKTSfmQt@OLfNM^(`P8W#OxR<0xl=ej+`jR+gT@61cBhpJIWyXP`|1@1ETpMpt6CEKOr8y?K zzC8o&2mzWXI@W0hDTPzDk%gw9pV4%)dMY&pM?`Oi&UR0*pwfXom4cJV|H3e%ENq=l znM5g&mOgI2LL<+%#PWoj*my)2lYQ|>LGc@-Xu#X`t9lAHxETLw0=auiXpRG9AOV$Q z#7~{8>9f-#u_MebEL|8f+wq9T{iAy) z?vTbxM3_Af`V1I38(J{pwjN9!y=XZhzxt%&&g|c=?OhK4NSIkXSW;BWhL&XAA_>{b z%#a2Jw=Q& z4d*%8Px-D~a=1O;UhubjT`QNDEl_t7BukdaqaG4kVy|~6g&zc_NPkXYNjz+2!bE;n zQX(zvJ-#muCVD}-nEC6*T^>>@H1Gnh>s3wO#mMW-xj~7J<0h=zkEv85ZgpDhm?&C$mMoxy zQVMkta(vFLk+>Q`0W0INqB;m{HWCOOp1_oqEic#P)%5BcUvVnx!8TUyO zedU%Mb2{ibDi0d5r%EOJ2cCGP+t^aRbMS1H(C3NJP7f|5MgLECnB&NE46U*c&neD( zwH@J9@rRjtvC;&9^xWF}y(h$}RB4eMuDt{Xh|Kbc>QM|*WR>%T3+FyBmn1{(=^^s+ zZzrLcW`A)zkiH-%fCpc*5bb_|&}ApB1G^Q<+fuhNhp zu#7Gkr}*`wXB(~^cApAoBKsb-6I40P*9>Mjlw`1~I-#snKD+}&&e0eVTS`P8R=~}m z452gWV*9;QZhpHpXdK7|6o~~2mdx-WaXBY}?g?jVx(NQUN&%&~LbpAVezsk9w30_y znlj~^x$t9$m-F+J`$zqU7EU}aLHD;5!Q73P!=RCt7~Dv0oH@QY8L>9PSQqRa_;MfWVeM%H7hE$X; z_s}4ks4L{7_+ye~nLnY7CC2CiRZ|d)qj2OIqRGjO4xJ{Acu^a(l=DL_m{FRc0@98J zwmdZtub8n~@NiK}Sfn#HHrj={Ta@LxvahtkkJ_pq zvJHw8XIBNF-42~3+4~;DP-O~Kri{Z*_nWyAWSgR8T=S^JY{z2RDnELx$~V&XlACsa z`v_OqF@widZDMsb?x+TtNtW(8F+kl8ut^pAB;urs#a^2=$Dn27m2sjxn)h}AkMEXM zB5QOTvl^y43%y$U%P`sD^g8W2n^DesU|;ZP?;@_whCDI}6fX^>UT--hKMGilZ0wdA zr2iFUj9*OF6$E{fi+-ZVjVyL*4%gNCD%$%N+t-7_reTF8ylUJbR1Pb{dk);Q4DN`f zk3ZX@ty_B9x!OMe7C36I|8t#o5vPwj8lTDh2%vr_&t8bV^~7S+P~yqCTMe55VsEar z{wX);e%d1Si&mUe7II$3N4hf2hn{j1PEghqlPJem*{c4IGvxyOswJmVt0CX(efR99 zb8hX8lC8xUyBcKpz3l}t`2AC?V%N29_9M0`YbfZ7S6Ve zp3--(ZDx-yR#n$^^Q`1Z_#UMpxK5gG+cr+XR#DaT}Rm;z(*p=A`+ko^);+OPkka>su@>^?bc?h%$a*tXCF3cIN80VLR&oqlH$q|&DcHI9E-7r$K zl=c=vcND0+M|B3oXhEzHpaL}rH+aD`21hkMZrfrAwYmF8?V6#`?-t{#u4Lo?L7t^# zqRZw}a7`ESAbR5;47#y+5aizau;vcS(MFC_>qZqq{EDT5Fu<%iSU2N=SF6*mO^Kan z2M9x`bVv>MEh6IL#&`wd>SLMA?#{zf_`_Y=tpj#tlS zWzi1f0?qPMZiW=PP>CLX>hyYNNzHE3CrZqP^xGm7YB8%rrO3xll3qP?S+}W;a8AM# z@|Uh&xzu)k&S3jFqpY0CR1CEjVO`GFBa3)utnFGdJ$o3Kc6AAV6l1R>==QooD~SM; zetwCiFxJlkk*b={?D+`8w=%$#1Zr{^CKO*{@0r-nnLpZsuhw$)x>%guxxv82B$Q)- zhHr2^${bRfj+3>BNE&_174}v^o|avTt~e)hQz5ETCfJT zQ^LbU1JS@u3%bX6af7E(Nl3hhO$SSslN~x~+31zUH-A|5<1ZJu1hS!?IsE!0?3RB$ zAO}K_fUq=_Ox1dhf{k9(`jQ=QW&6M#FO8WgGdw&5&O(eLO$A}3HJ9cv$4BoevIEcF z_XXvGr!at;suFuVeucx>6Px>cM?#z5$p?5f`zH6h)?_hs?e{aK)w9U9Mm`PmCy%=e z_qz*JnE$g?E^t?#t5472Eb5=NiS2V@HjJbId>7H<9qi2Oe5fS{JiV?~VB3{3S694t zk;1c=)Q=E4Y!4mlv#psBQwigXVqdnJ)4!Y{ zE=_a`=WIkH-CQaPjVIj4*ib*pn1KZr!8hZ{=0pgnBK(ID0D)I$I;6*;`YqVOSRcSY zKTd2KWDN#g^>0-6S?SDNx{2`mCz?d?tI;PHCZ?=Z;fWDd2`g0*F=cpKVOSinD$U>{ z=qmuv1hZvqfWpvaJXF;gq@p>~4B%|?&7%Cm)Y$w~tvYsG$>#Om)$z37MmN0EW>1gJ zGZ?;RTnrgjBT{-xOBx+i38D^3fE(a*b@+4XYIf_KdGDF76F5G5MB{ay>&DIE`Y}uM zS%o*xsf!Km#;TLEqqp-qUp3-m@zkEI-!_Ol+%tq|DOQ;WBiaNTCivhN}DyJ*&TP0+EL%E zOHsITsvpDVe~6<^4mq+d5h&{(cViJHx{Su6or^%a&-M;q{bFWg|BlB%1d*wt(Xzx` z9H0C>Jh%Oc6nQywXDwYX6UMnK+C3%~Lp?k5?fAm$g$Wj&^JDAF^UI}2;~~hZi*`vZ zyJsv$%Xy@({1Q1MTGPv&n_{?8^MX{PwudHu0LE`QnN6H0YuaMD%A)?WO#2Lru=l(Q z*FkMnf$L7>?SxP(x86K#tgsJ^QAgq4j;B_e&+5_!g5I08XR}M- z8=IdEt(}Q8=@`Ox=0SB{^u#4Q6x{_LmYG8|Bw5x--YI5Q$$Kgp)PQOf0tpC1%~7Tb z`c?`?dG`B|O(aXi&%}D5W=jrMud0%40U*PkHnUCQ*7@PV+7#4jz_B?o_l5bQs&jqk z$C{Mcyt<=_5aD^WF5UIFoAYiXZ>7&QP1Z>lso7c#HjCymE67ZtAL`NBg)>By?pTiW z4tn{Q&I%^kbgH@N6z+;tc`>uC?sUi7=OC%vTb zXcJL4Rt?>ai5fjJEUr+4uSVZ4>z8ab%ija_3Pq(TEA48bGrdKTBAok>F#-eM3>he} z;1_CUuZtR`(AtebkQZ(N3@^GxyOFt)8E!Qa?sN6v_004oGU~IZ4JXSI2g_KtRe@xd za9`Z#R`lPF>&VkaDcfB?r7*4*XqGF&DudlvJeg;COTB}%@+o5si@>YqB!<57BO|ja zvdtMsm&A3LqtncA@akOW<^r?!AGlo0j? zq0Z-rib{11oT4!F5sf7dSKc=kc~Gk-@Cmsjv2xUWl7E{p76Igd+kO6YJNs zl?wa1&#h);(EMd)pqi2INM^EjnyZ~7kkZfhXuLWNcY8A1WyBld!CH%r?B%ph?U{#p z0zBjTwgtU&#+|Bx!Zd9fW=qdRz*<5Sp|E}jnnh#XzY0tI)%jZ6{|@IfH!~UddmzBQ zNv`62UktSiAh*9UNgcXUebB?^=;nm{I_VOE}kw zvqr&QRMJR+_V?&>p~oC|_}7TcI;Vf29G<2ly)>tAOJw2edOMln4;G(9$IY;i361Lp~{3~v0gA?7Xn*1JS2b|Q=yTLD@4 zfW@JakoU+(Vbnr(C$6@nV@`#EsV|?f|N9LmEPl^QRbAb}_kC@FhMJk_I)#^X?5q)5Z`d9BBnQiv}0;`y;X5B&mHA_d1MOGd8}pI}iREI|YVp6~Bp zTXfWeND0CciocE(OR|_@NiStW-K#E=Q0r!P8#c%d2`rkM27-E*hS(UA_MH-z2Ck3t zQ@H{ZLj9+5B}8T1zf6To$a23=3Oo&q$n?UZoQZ&6?L08*iEFPnbr)~sIb^Cde#EGym zWSAn)4^v0au3Dd5r|6$%dxn5}QvbMp>sCx5|FG}j1`QmOAF-biiU9%1HZBdd+W;Rl zD-=yQhcI8RYiMPnHR&!gBCYs)Pf|g6{Dbz#5+?hdF`BX9aNx`sT|R|YhJiH|ht6=~ zr=Dr{hDHL$GsE5;*iY^S#~-BnfTU#3Bn$Vi+LalvoYCY2zr{;2-zR%VwxF#Wc<;l= z;f|fhs)$9SvfCuXqUujJnrHSvYm&cS@Txi_mwFf(JGRJm1OK+jb$?L7hpQ|5%Z>$0 z2;Roy(ER(rWe$6A`s&4mx%~m)SC;9%i+?q=JKi-1lnzCdMIS7(BDz4^V3H-XGT1-8 ztMX@#0H!(-1nb;%mGXh$N1XJCJr{u~LgfJ^jrQ-^WNny{jsu{ z9dUXrhl|LMf5xW2eIbP0_Z+ZEjx&3va>o1}+;3BY&h>`UQ9B4DP=#_+O}(_oJuz`! zPG)+|vGndHTi3_I!Q&pQ1{H|1>5-4&G@JtRJ~{$4@Gtbotp}9ps_E-7D6~eO3ksX; z%*OQMs%h=l@|P(W-?kO&PKNYCx=2o~s*|_Z1CilZtyTYgyh`$E}pv~rrQMvp>5&wb^gsB7GZ&)Whb&_rw4@FgdaHc z5TfZ9QZQ&%M&@z8Bemo7@z!|5W1q`qe7TUiA3HC?-Mas;b%X;D2*pUG^o$49h-Z4N z9zfI+4@tJb$;vbAI4>JrnsF3MB~5441^e$e;)M-eJ%mz_(e{B*LqAf?k{UzfAj&>@ zd0Ux3IgvHUj`Z+TMb-_$HY&*cN-T<+k(efsWV!=aMrH8CZ3AL0wV29X`(|$I0Uw9Q zzNM*$UuN2zocP;t-;3D0$uVz2V$OQMWdJ#y5V=GzH#&obrdrw=a(lc&V1ozhH^g27 z$nDl-yfI<5SdD2?Va47FGbM3vLaPLIfLpW%eR#Zz0|1!^QO@~Xw+$vVj?D?~N@qFT z6UBQM_>hRDi9<_{_6MjlbvhdF#Gw~$pa^5yhS-uWcnY^X>Wjr`mcbgsuo&Fe=oUj{ z2!@SXw8z&PmFu+l=x4De8x$AdxLYndH>;fXO9GHH6oaLQ=u(hnZPc_t`E9@h$$&_b zg-@;phNbBE%yc1KO-Eyk`bUON;D8TutN=311+92xb-*G%Osbc$VfhBR>U0{Pa`r?} zNInswLGckahBa#NTRlgIW)`Kpe8n&EBG4Mz11EzYEb<|nQ1)m(u5g3GsoVMv`ZlQM z!dPwKZ_y|jZ*Sjkq^L>BK+<&=BEYw!E_Zmg&~1hdW%jhG5ZOH?YSDae9sG|!p=6G+ zC11#h#*Yprb`lvQ{cJ8D8cuX!BnAVxWY#F(+r++mlx8qeFTHmE{#r?tIHW+!Es9hK6u51dl|fGyMNdz#CwvXtV5mETg&or-x@Toz~1kz-%CPjYg}joZ>2(S z%)BB#-u)Q~UUjP!!DK(9px=K!4HTQ3WbxRQTKt{CH#Rm1SN2QP;UfY&S*;LfnI~vOHQz{29wlpdIWpTrVm%9qtU?UMr>y`*OntZ?7Y zJn0^dpYcHG{yuN}S-#7f^`;@_$1-Q2v$fUCx{YY-u2o#%xIA{F7Kd?}K;A`aA!lf5 z=E_E9Pi?C(s6`G3a$`a~yXJ77<3v6lb#CGhqRrD=E z2oZkL$~fmwf}Yy~jHEimXbJQ3{6BYQ008J>tYSY+y#M)-{e1gvYfa~3?qY7_Xlv#C z&%4}_&cex&&eYh}+Vr13HJ!PQk+B=y{~j3L`f*UhfAj!WKU8eq|NKY(kpw6iJN=Kq z?!O%Y#j3kZ#g(uH~ol1jzMzvAoaA(3yq}0$xaXp0e-?%!QqJ= z0NMRy^?9VERU$v$x~%6^_j3Btfq5iBRn+o5wtW6nGY~E<`j_eiFeN)IG{JG@IYb~6 z2mfHP^nBhH(jQBJ^}j3uj`c-~Ho`QBQFL`)t=mwwwkL&dbIqBAFOaJ29SYd7$F zbnS5z!kQ|)QOV}qr4LD2SOzIs<|YXJ=TplAdk`rd0m3LK&XmOg4SL`9`+uMYo7V1q5a!%37evtT8TWT1-&&OM#xz3GgkILSb1VLWgxM(aKmJ98pPox)x{e1TYyyFMI+3DN zOC$Yi3GIIJ%680}G+z%vg|H8Gew9ukh#mFZf zZU`hGo6!Vkg!A6gL`fwpwM}McR49h15k}YDJTU9D{>3D}H36f0M#qO6vJXtAEMZel zsiKxvn1H^jmflYU#-EuPNNgM8hAvf3LGOG$;Yjw3%DuL@xp`Bw_1%vvvgL6w!>vO? zBt+ewQ1>Lz$&6L*wn_HhgA<0C$_Jy;Ukkwb+IX#xU0VAJ*GJYHE*sG$0$3^3#TI{O zOPl?km{4t`Wa>fpcfRo&lD=oW}C zE$SB%vqv|IzSLOOaCkMWYcFf%RI@q|*ILtV32E-OZ+mJ~!(y(>Bi57EV!-sw*#HJB z5ACV|MSTAxFTiQOD(2xXtf@c01F+@PvhaLoU5V*;T4z2TmanL=s%1)Pq_Km@Dj+@2 zb*Iwzsayfby`QTxxDi6lEMb~?{zAIePgA;*#Y&-}`i5TG+@y z-~SDhQUb( zth{M+$od1Ji1WqBqq`eW@X~Ny{OOis*snxt!$@5d#WJ%rtQCtV7hV1Xp%9u#Sv7x? z-}EcqpGxO2HF0ydm)OmOPes24Tmaga%HXe{qIqobFG7P8P}lEkP8!RegK~n?TZ@rS z(1=9S?$-`OHzutYJ{}8-Tc!N{^@E^5#X1S3!T2ad17otoa*>XRQh)-}sux0)Zz-#_ z7TG}&3x%QPyqVg|{Nb&{=5zL+ZK;ll$j0Jv-Tz$YmQ;>}S?}*ztOU;M(+BMi$r{C( zX@oLzt~L>PC|bcVMVn?|3)gYFdYaflMc4cou2f71IEh)C(T4z))}vVg^Ow@cux&{O z%z#`&OPX}azwd9mLc~bQVYu^%2uY|fqEWbm_@H7Y5ZJR;O8_flew$KxCBK+H* zBHr#x{Q_n-Ke(7zT8&X)V=>{=yyw7b(*9DzJobe?l|6uwgxkc#&-+H$A7uH<19s%8 zhQxRdcoLFYAy`DZ)4T%)cI{sfeSS2^68iWA*WOv6@|D&q-Ma*rC>6J~??zcJ|cJ9KOIkUS&u2e>75 zqCWAAbG17H(XfXPlrWKdbo7xA~AX>fPdZOyz`d=3?nLJaR1Sc z`vs&<_8?~GLtLADA&*<%QO0v(dp0}syQ`6@f5+s*0K+w9-pvUbJ_AfL_)`vt!G0Ie z{)anZ$5xoSiSi3R@!D<#WpYj9pmN@54trO&#uvv6$7$u$2mi`T;z?a$$oVYRmX?lk zq_>_1DgM5C@n%NUsXR$*u!0DwsmND$Q>%9R<-$HNWWJPGlZ$#kB#=xddjZN6w2<^= zGIXH*j<;}%bhMlgaVXdGqF@rl4uCtd!vnqW8!Yb+lgFEn50QhPUWYRH;duJYj3V%d zcZzRmc|M7P2vxVd6ZqPbZ0nz9d7uoctv6j48)i1u2ky1ZfPz-P6q71rH_8&ZG=)5| z@7+wuzv|?lU;lQ|wLRL1^XDhHDE{&1^87zOqW=sf|8>1Pt^Omo8%OnVP5vHSmstj* zW~L{f;1qQUKpn57(NhL8K6E(CPdCpyHpp;d)3=!X=Gi^V!DdWJQpO|dEXse^?sde? z;dPJj-8PlWonyk9CH>^Yn^&iEE-}@@s!mEQqK1@oN*N!b$WF{zs5Oi(lvbyrX>Yg7 z(W6O8*B;y27hiBnE)C!dk9ttV>Yqx9`dF33Oix&BLQ9tfRffnYZ=UK-N?!_ffwZ{Y zSBy)J2?zC_dHgk6*eXlf)++TQ(Z$nn8M@RRD%Z7%&X~q+mfc*sc{F?r)vQbs<i3|em}RU#(U;Bm$_IPz=p5D6bMaIUCgvS={m#|h`YvN$BAcB&}R;td*H zaNEshhMwfV3uU{hJ*x&$4%r#Q;Wu(>&oH9AyR=mya)gGUrg>eO@t-!j;Fw)cQ!s$Z zwiHd8#*b5OgMV9HP=}M^i;#*Ao`N$MqS;@A4#c3h#4fR=06r}_it2$Dy>gVKd@lF{ zQ5?(8)uo^ziY!4zRa#kfXzZ8a(~0qYzwaSE_>6zny1%@9`}uMRuw+wXPlaes7x`C2 zfVqo_$W<%tMx=|Ylk*dldvF!qmTm-pJ@Rkl-FxGF*bJZcd>n14+F|74Me86{#}9Ad zgPt=oOjI$&4qX3PK)0VKcrF;MqTET)xCLyVSkT76EM-Bvic~OHoW%4zQosEZzA)p= z>x2hf3HI7=DaqlAvx!05Q2fUB;OcmZ;bu-GM;Mp$3p$$gC#*Li5{MFt$HRT(e9+Vy zO}YsY?)JeM4ZTyiF)g^5uXDKRa?(ET(^nXiWj<%Nr=w*2T37m!7nywsaaYN{iGBWg z5GBrc(EBC^m(v!KKWI`2(9Jy<{L6sb%TYrsCqk=OU)Z+HcikMDgOC5!{#>f-{G;e7 zV97L+iu{2MymY)j{4l68lu(5H|_;|KR`;jLb_DppImv=|61D%+8~z>79W@eh1gG8>X<-*@a-ssNao{JQ{* zLbkmyI=T0eVyGz|AF43^Fe10CJy=T*nGG@`l^>O=uEECQp#X3Q>Y;*B;mUA8&k~4q zRdEw)76O$+U_ZYj#INCMwzm^SQ9;~v5<+j$)-ms@jK2S@6O8D(S^Q8W8gXe?2)jWp zcLx_KB2@L)->3wwCk(+~8lyI?CjF7Lx&o8JPVf9A%grw zV!0E!oRGUFUb?{(tF<)cF=^0T2^E<0<<7Kc76IiC7OGsX*)+`Z_LYu=bOS(;0SE);O05| zfhgbOQgk=U{0-CQm;3MEkCol3Y?r#0mwa13b%1(FxbrPN)_BmpVZlBshy~#4%MUS~ z0j|3JTX-(~d#YME#*DcJa@<82YT)21Jj@ujz=t##`HXLHf3M(}ye|Mdnp?Yr@#f3V zuHWzQ7OsfPs|y+iBBaT`yu1;%CtCWNGpV{~auRBZ>SBp$W$|HVo?+KBmAfRZ zZ5RAkCwmVY7~PPh4%G;DOzSF3d^nzuXOwVlbEtZUQ7~l7hk5hI0-qo5Nm(azeeTUt zO6&QomwJ`s#TO$_CUz3CDZ?%eUKwg4S)IlO3Z1(9!|Hq?`xX){TFTmSQ@NsOQqkv* ze1{SOjH!_)34&4~x`HTHf!J|JN|Yx$h3t|4%0#1hehH0hG#ixIwpvPAD*s6a47PYF zttT<5aC7XN;6`6xqT_T+Wx=ttxUw3#;f4&_+@z zfqjJrehw%<3rz)d{_+WA>qt@Q^0HoYOZPgl#rajoN1fP42P92~vnyFpx}7VIR{5Ev z_d}JLFlEUKrX$;F7j02?wQmcAhIbO3AsjmmLR3I&|8r$W3ZGHHBS{9z3eUQ&GcQme zm!vbVXW8miCL00p=_(5vjl#M4y;9CoaN`Qqd;7inzmp7uVz-f&p8?+v9sq#<|8atl z_$MJT{+I7CSxv`Ls}<4vA49Ioo1o?rDuqZiHfPQP)}(@Hib}lX$l*M&8=W{2n7<`J zDs$$z?~TTuPh3Mr%R-beK;$R8rwh8**CmIKoG!PoD4khL!oy!HNhU2#>r}1`RZ4Ni z9FSvG%XDyK*mclPYSy)d0namvD&er_<&Gr>8kxS7A`xS#eyaTOlm*D1%kgo#WQT@4 zeRJX_N)TxX&5CGdg-OMY-+F>r&vxWvlYKwye(X+`7mRKtju~$X$L2nuwz|JC68 z{&M$WKMddNvy~kGgW=9h&yEzoiFqz_fS{61iy9TYTD?kb zEO-d+~K9inEHoEn6^D7t~P0^%n{6uATxhJ*-ciBmERtxGCPH-dKN8vqk9EU!& zcROz}th~+>lPCQCQ$t$u^fHfN8#Z!D?TUW+*SGMfg z{UEN)sfz>Xp|HpV`Eor@Wob7pwH zCc}@v!qY>aMt8M^5}l47E^i4t8VD0ZZ@FN?NKYuo{hXwy)1YW{rcPhiT|8X~q9_M_ zb~g`RYkZ#WJ|C_7u7fVFo!chZF5_%?#_c+P@5cX{7LV~o77G(xj0*OG=H{1IrOGB% zO6QCB#cSz!FE#zmd)s1+J1)@Tn=cekp~iCebe2K-||r@&`*z+AC{QyYSr~& zxz{5>O~+4a=iw1eO6~_3%0QtQdbqV4IsN`EAeS(lkdFKaQGLal;6do+S7i4ZaU-CN ziP9-I&2^VmBD1^X!A4+HuSaR_Fn%Bn+FWHdY{injyma7>R%KaAVOlG247wRhYeQm+ zl`TYT*J^Df&a15Nod+{~li72?KzS+!tKe|2^7;lqTpRn*uSMzoE37Ko-oqqZyhrEf)ev!mn8AT%~Tr4;?zeuOYC$ zT*@tJ-1i(wX&S(A1URL9Ht!FRlFbLJU~5%o`dguyM~7D4bn0u6p5tfn(Kr}Wf}W)I zEve-9gE7SakNcEGWfSVR?|LmM+)Q8y z`vM-z$J2L`42c|ds&j({55#^dlpE~(hfi7R4rX^g7UDOyt2*T&gvV#7J(AJE0}3%< zJD~|dx8y1#%`{_Gouku|?UEwrdl4SN_C-Ho+oji=*b{oX|!4`b+}31+fl1eQ<}0tDGaeKCw;lBg^CjGCM~Z2?gNVvwMW)}|&ZRM9 z#Y?M>*O}LH_5A&8rL0>y>Q4Y?qs-V&Ms7pw+{Sm^fu=4@;=~Bq#coWruM9u{8Eg_O z+Iw9-5gqtoOFkvUSUk*Wmkb3wRk7!n!;67OSE!HUkU2dPM{TON8KQ#*xhJOwxue;rw5v`%1>9)<4|U|0CVku#WvX3;41b|IRN+bkJ|? ztC~gMH4y8^t`%z1twr;SH+~gjTIAmxWKPHH8@u!EE$#SIY>MIH-4Pv>Us}ih$+qP}n72CGWs@UiKzTLae+53OG`}DcUWuB{d&9T;;bNoi^ zdU;q0=e7omz3m+aGwz&CKlHLn<>_-3hxIG4_qAyb}Ev!#vo#YPL0_kR85y^R7v6fFtFCoi&eXs zFMSR=4U7v~SRIR(3aY-sihJ#yzT`>+`W!cqL(%P%_-9F+M26IggMR9r@*n&RSvlxg zABodtRx#qFs`uaWn~+QcpQvKYf0$by8sI!t!OAZXB(B-o?o63^edK&%?rg`@n%;=z zO{-$dkrG)O+xJj}g*casH-Ij-5^Z;NbU(v!AEYK$j$QI-+H<69_htaZ@rpM3%*2Z; zQ>&{JduM*Cb0f2LLCw3MJSiApo!ZdS>F@;3w}h(|WkjK3O-9)!K7@!EMyei^CA!ZS zb`WiWVh#ObsHm4sS$3dTnZ}FPkS}P&zf$_aQ1qnGRh>n3%#%T)-zz5fCBifoEXr07 zg=DB!B$os0d9RomMY$6d9&#dpm<28u!iUtI86$P~17<)4grLfCEnhS{WUW_<@zb&# z%k$diU6cNY+;nahU*QTibn@zJu1Ouq9awO>Y;s;I#`AA0Q8`^cS^}LgVc`N7`;+&x zy0^o%g+^caF9sU9r#Elk5vp{-LZ7mgcemTyKGC4uA;tbU=fqPblUFmU3u7wQ${xXZ zA1wP7q1Q9!Lb+38@3`R)vBE#M@C-W=$2Oh0s)JH(WSZ4)YEgI&%eZQ!)dWA=$+pNQlftMN)yZHOjK*X=yJY1Ur(jC^|?zY+oOJU6G-g?NSn z{NOjNk)`?a2E6pFlNpJ9L*J*et%+`wnuo~95j8$lda_teA7zxF(viIh=!*h@HRNI2 zW}UhPOg?^Jx+_HK>{QO2JINI|&+CE(Us`E@Tm0matS*O}(9`#WGKqWrXC$Yihmjf3 zub1%SJE`ru>nc8&{hUz5JEoPzJO7XKTTspy{I>ck`HJEowNk0{t)^6qQWy-ksXJkj z*o=CPxtj0SmV_0*%?6RZ?nbIb?^+b-q?+{6YM%Y6urX^vu>^nvKf6}if32H=5_K)B z$EXaISNioKULmr>6aP}7r(=|0`S#L*`$vF=72_9AcZ#~lX}mxK=z###uePh#CUnS+ zJ4Xq65wdv8qMu$s$anGQ)bkUxsQPBaEk*@rN2o1T{*fwX4(2u}3>)m!%1#L<^UdEq zm6fHSl^vbmMzH323DbWjiL!GfUd^7bXB(;)~{Lt9U&0~+B+>D`-3&IPeu#| zYImXGb?s&(i*HF%I8ZE#-)FUsKhiHl;KBzVzOnY}r5cxE)sXB)-);RS^NwcV5jzjlj4LV?b9P?IB@qdfZFNw_AP@C6{!vxPwY6?+WUU@ax1dD^0bG_( z+0;0-$PZ{U$G3ZBz=9pP8=Y_Jcr8oSe$x~>o|z)JZJd6 zs19tS)mZtjkJ>mDB*(DN2>HKQF#U)^3oSfV%R=V0vabYfmGPs@Y*a&hW`0$8vFH<-8J=Lk%HpOeAW&7FrG_ATdZ9X zO$1*DtOck!mf!?>-xhR0KmB5fq2WV_^3f1S&5i(rrz9_$sqP89lw+ek;H@p<4VP|R z`jqutLn^SDN400f<5q0e7>sX}Yk`42z|+3wPkO)hz>gbmr&*Fn6nbYz>&SvV;R5?E z49LVn12_dh$j&T71u8g>qd6NH^#WB4G<~?P3lW#W6>XdH*{h2 zCN96r47o&BE*Qf`@n$AkKq1-GSc4ju>oagP(<@yJl_J+KVD{B2kBo*;LU*XV#wa?i z`=7YeRr7H2!8bua-&M$cA=w>I5reHf<%YlAmU?gPkL|)WxCrdHULue~RNjYR^f=6U zHcmc${f-PysaKW5hD{oRBAYpxx%>5ebEh*|mXBLwm;EIk#XtuV7u0 z?JYc2KH4cZq@TlbdGk{Yzg%>F*y+HP87EQ&|K=)%o~cL^xHY_K?e2 zRR$CgAoC~!4}qx8YbAL%^4R*S8UC$*soB;ue)bijkVCy9dA%F)9Dg7PBrE$7L-oq0 z6+C@&0;B0JTafB27emAQS78s+*z*@4Z|txe>9q14wOyp8T4#a|>+7~5mGY`(pnaUL z)SS*(Zkp=Qfj#pfsuM;?lqXp^Yg!LZt!_jZ6O2rCStHaK*Imu`yEQ= z)^=9L|Hb}Bx2m=61}owxz!tw3;$5#KmXc*WbqtqgNKzij%s^u*LT?2`JBO|rp)4_8 zQ-9oh6O&*?9v$j}gMJbyzJ1~7is0^M7uI_{w+D>d$9!<>fjyv%p>aNhy|4JMoc$H6 zaLvCt`%xUVbbkc@WKoA|;vN@EVpXSsY1<8muLbN8Nb0+NlO0Hqh-Y-_(<4mLa;Ju= ziM95hFAD_>IbBI6cf04QgL8Q~<9FL>-H48#C0algpU6Twe&W=R;x>g?p9brL^+ptn z6D~L0>stwzVwI2&P0}}(NS}l_)_Gj3PTulXIYUD$VLDS)F&U^Le}_+#>|A$)X_p=H zlF3Y?XXAApk+FfM(9qi2>|+rtgVnMR<`>6IWuio`^`S+sp%K)}&(qE%WNmC-=_=CO z=frGV45A3$f&6jX^{4l6SW(?WV9~A0+Jzs-j6qBtvkJJph2fq>9Bhj@qfdvvg`@O) z5?i)DrS}l!UUD3=39jVCN%5r{4h5X8*n(QCs}kns=DiR~3 z50V*nBa?JHOUR8(S9=<)ZvOPHmlIFMwi0@3;n|c3?!eI%7z91ObV=df)`K3%LFd>r zYY!BV!8A@AwO($Z7w8ji^f9)AOgmuINelJ#(;?D-9F?)<@aJogp<{^;RK$ats)|MP zZF=E6`X+A}2gMa<9V4^B>UQqZ#>U!m7btNu7#{lva0!62Kb%1N^T0w}PPQLD%ko0} z&lJ%2OA!Sp-0UDa;OqT9vhE48zaaZvE!k#S3xgkRNQJ$oCc`&s_`T5(fHYr;8`v6V z)}%jo1YDDYSD1hmj)1-wL=00U_HNECT=;YYO3%Z#B`&xPa?{e0ozR`&%q@Tio`#ZS zLl~8Wv-IG4PoOX1$*PYb{=Iyb%M_F(*-QUtJUB?`yeRO zHj^Itsy{A^wa?AN_}Rqi`?FBNpk~y9&CHq__UDeR^MkHATy7n^E!Y+F->ir&kZ{FC zCqtSC$Rr3)*p3|_Zy(%@&S>Is1^GH_>!+wA%$C^aH^HR%XWz$})yy3`u$+Eb4n4#_*LJ|2aexF)d}2C+{#B0_g5|DtC# z>%1_g(d71b`6dfC^gf~EBZ*p(N>=Df$%c53ePWMov8Iucm{hY7sryA3#LVU#AfQ~{ zrQhC49MS8V?$59`8-MzJy^XJC&s~tN$`ASIem?su)4bM7J=T8n zOFW~5ivt?ooc%dZY;Q8V&rpNBEgxTvALVv(kXS%Qvd=Z0Fgb#~Xu{2-ubB6FDxk1t z6!C-I%kqWoGzuR<0W$!FgO@A3{oOILJc7VdsC_sx`ILDqO}?dN=Yhv?Fd-*%hBew+ zWtI?ZX#~S*T;4MATQIBd8{RilZ@Hk+FB?w-wmQD#$D!y)i8tBrF?ZSMil%*X+Y;Z~ zHFhKSF^Sdzg}4;BMMI$)O+{nQu=+F}T~7m8YGDLLSRU#eN?1_Xa|8J63*=vW%<=n! z?e2gnbt4K8kof<$=E?{uxf&bWNt@dk|NG1OZ%lqW(qDB~KjLjxg{GFYvu9$7R$hes z#OW4?=4C$54A>$yA?-53Q%bVo$Ea_w>F8Fn(S+zO0CYp)9KWO z+I`5dqeO3%7&B||*rb(AVX#9&5~)D=7-KrSF>^ZQ2T{t;@u24UgiM%|2NfM7#?*Cv ziFv1dr9L?#Vx>{Vx`txK&ldS&=`k#g2WcyW7NIzvd4AKPOU)u`A|Um&+x z-Q4S8yBzGTB`!Q;pL!!keJk@c(?GjoZBt>xBK=|w}r^<4&MZ<0oJ2EE^V{vO-rn1aRSD2sinW1aLBrSOa z!M5(!T(EG3qz4@fa{SzsK|=XprEZz9MI`D^Dz~NS_{_0&%bZ50!f`B4wk!pguc)Ga zA9i0#sR?6_n9hgrq9!&TF+dIG*i9a1 z%n;Z3$)RmzZ3{YouMY@9c(sx4u8h6UclF%p#P;P5Y|6yBlvG~d*!NMOk3N;m-@?B3 zL`e+<#FEUOb>ry3ju3D}-0eME&|}ZNJkUOzmbh}^Ki>TCXmxUQ^>pOH9uy{~yl@H~ zyq0oLyrzsghESI&MxkoVB=Hg+qs5(4%m6fP&rrgTrFj5V7^X44MiXKm1;6MGnC(v#E>2@VOR<)r&9tUbA7T8Y368Gw&G~GLQd794fw< zl0TTP2<@>lGO)pr-NR?i`X(5ue2I&Tm+PWIZYzh-d-GnrP*tN^={ELbZM~44fy0)d z+eanrurW;nEP)U8_0S4dam=)s;r$0_z>i4bSHR3~3L364oH&=@xls-4m~{e^K3xRIukkZ@8Te_FAi}dLMq2{9 z%gm+%tKsaJ14rP9m)1$oWXCiWB6_Ig%`xA}%<7e1?G*C@vu-8+bH=_B;(j~{T>!cS zW~U(9xi&2YL}Pc|YRZjP!SLb%C>j}>&q&6BD%IXG)DNCLWj%fu5+w_=JF&eQ;c4jB zL-h}%onDC&|2KWL(ifiqb{oW({UxT=(W0ywZYl;%jcH*~bvFO}yFdDh2NKzb-HBQ)z~c-FHl|N7YN>v9IAyTGZ(Qc>1^7R$rY00FqDW(6;_P-K?@U|w zhBM6|=2z^PP=a}2$b=>k-*0#>2uX{<{55ymsGN}%xoKst@6I>M8iFe$JX?T@pT8YC zo5nwwa!VgfYC_3iK;nqKE0ErDj1-`I>(A8^=13ZvE8hY=joRUWDvajay3amENp<6u zs90xUCQ3Mg_B(geR2Er9UcEJjl{2rmPVo`R6rKZhk2c6-F0wWLi5Kr~Us0*-f1D5k zrL)EFkOS+$lRH3U9x%O9Z(OK$>7e`5t}KP$(zRe^aj^XO+lxt5Xap3ehmyG!BV469 z=$Z^|Cb%0!@;P6{GGQbVBUE(|Aus(WWV;4L-7Rt+_E1r1yp3vx$nQFQ!aJiCqNU{XZit;=mFcEs1nHTrcLZET2n9G_ajriOt{zI zA|Fg;8H>eoo^_vBY*IQa2mX(M?}lOwQ@LrI9(;LZbzopM*p-_fF_q}1dW=Jk>LQsT zTp(gqN1ImI>#ZF}VQckG$cYU=)5LLL z^~Q+kdHYSm1Q{GZD6$VB4n!&UQ)|lY+oiRnyuH5DI5YRX7+CLmGDFldznFfNO5&tk zC|Zv_gTCZM{|2zhaeA*=rKTB++E~^f@LBr!0ugb<)Hb=J2RY(PD!Nxc$Z zQ_8z{$jkUi`573WwGy!D9&y6bR?yqw=TJmiyM(o|^Q-&WtZNuWck%XZ*K6fhrIfH% zS0nMxpOG+6gr3gqcwCWL(eNWk;vIyO*k7>C@%wX5;0}*p?z4<}G2|$^Pg*|g!FEw+|J2cw%|qAr20K7BFV1X^=d6vPir z73WYfONhSB3J$4nTNoah$%VdHscDxiRJ7)pS6)lKRlF%1^3W)_+J<*{N-D=ISls7o z%Y^QopG0ZIPtf}`tp!U<(_H9!W=KgHeWlqzKaLuCdareFB zUqLRXI_qW@4=U*#`dLX+rduhE9g4~h(Dtdg%-@`5I`vEg<5(RHe`$LFK-&Q+)KVkH z95VmV_O%4$J&u2Bd!p$4ga(s0D^_Njqas;aPJUAUKJKlgxs0I%3^_T>7Ch^B83M>c z0viI+#m*OD0?((X?##JT81c9Cp&H@hK-y~z@P)7%)ZyAaANt?M@P98T9D}i(5 zG5Z$u9cKukPR^ckJRKKXu@I>UE8U(Qo^8FIc#ld2`ANz=jSebXh! z*4Sw@)(bDW<|J5VPNgCt*kW-x+0N_t)0{I21RlcK3pTBun>-g@V)yiyEPL8Z{?viE zlhhmO;Id3#siIN7@OorbBPhN*S1i<}rlq!L-6BHieqVs?A)g8PW9xFT?#{u94W#T| zK3i70V5Ig{4t5Tno2KdQ9D=X%yAjz&K=jQ#Y3HFu?EEBPOaAboQqV|uP?;$1MJaz~ zyZC$ZOB3o4jSA4&frwGJ+y0IvnDfF7mF20lrpGI6hLpZYsqK%9A=|7t9SzOnaUktp z30{wTlaWkncBp>vx+Gw6Z4&K~=SERR^!IT_atNRJ7v&D9u3#lImRaLGeGhK*STpw4 z1Y`=P%?PE%Uo*$~OJ@ht0^{x>x7G%qj_VbKJq^Wy8v)QH%+`fisCSC|Fqf4pQM<@0 zt^x>plnMcc%OthO4uR#Mls|~@2S^16cJJc6yF+RNIsfq2lMr+t7TqIV%D-lB>9V_m zb+wDrh)?W{j-oyMqO{bJ)xe;!cL>m%ySnV$u<_Xzt{B_wm!SRTiX@{7&wHaWU!eQ* z_9Ete9)zjP9e5enN{v38HzdHnssuKP*0M}9b9mVz{9;(@#8X2}v1aITKz(p99(Vni zApS(c*-zaJ4^-^4DbbVY;aET|J7R+l0gk50{frmo)P*}*Xg2E0awa3&rg_0Tex_LC zj~P;yi4sn0OYDi~cPux2x!a;yZUGXNCFG7!Wlof92<=h2YK=reJ_vg=(svN3VzTii zMzhZEz?hUFG!SX#<`_c@9)pOM-L)#;cVG);S`TKzc)Nfxz9#BiwvGpK-9Dv}us3!b zdUfIxq=V-vk?=xvv+)xaR!5q`@21R_0tCgjghPpkawX5*(tVLGy+RtSjSS-1> z$^(=$M{&Y=8{yJGQT$?Ok)hocXGkeS4<;8o1+#GIG6rVqfBB%I z{iUpdr@#9-ONTk7nb9N#cG75^vdkDx@`0cCBSNc9s50lg{B!Up#*r_=&tdXk=~p@8 ztxmI;BXTbv)DJ2}F27S;EuL3Xe=&hqGgh(CFisubIwn_}@h zI|-u^+_Y+1p|l(6UHxdW;G!^UShAjcbV#Rq>Ry~b2&(>Z+@kl=SF$TXEdr*%N&~z4 zVI8RZgkwPN4q5)to8ac?8qzh`Om19ev-D+&Y*>;*f`lR+!qF4+y^jU=t|N6A{ibQG z`+K(MaJp3&g*VqEE~e%BTfDQ+K(o7FKPMMf`7#GNUJTj1wNM5N4E=7%xDn+Wj*Mb>9TPt)9(>gJ}L$%ohm2sznX2y0oe)_W4>?!Cd1YU3Q>$+hsGSithSM+Q1Ti^cSuUgjJS`-2Nv@ zq|hSQLeT5=YP7FXE{X~BPLGZsfkkoi=j{eizy$lfS(Awzu8EO;4}J%OP}E?788qpz z&sj;DB$0!tK_iqrdjf!&pH6DqSQk@lW+`PS4O{rl&FH06%r1f3Ss5PS#KE*Na{2`5 zD>4ib!(qCAsdmID^;r^V za$Ol9>4Jp}pEFBcLM*rv_P`CR>CTpZnX_Yu#3~Ox(0AcUli7K*h45}ZjqKf?>n{=v zd<;B)+Au-^{OA~Vo_}p~_wx+oNd=b;Z!!|ffcF!(Z0Z#-hJb6@TX_%0XVs$!biCwh zCsU<&j?a~Yv_cv{gg4Osv`oO9e^cqz&Z;<}#UxXh4Y=bYJS1UmOQq?@_l(cUZq~e& zz>-SYc?G(%DP7<=%WB>M<(7Umc3%{NZP`c;} z+#A}UAro0n&csQ){055)Dg;dJ7HO19)|3r;b^KVBXjPdPqC~8S)55{Z2@#X_u8ip6 z_mDZe*{*mIL<8;oT(nb7jNRG1`;nsdvmMrmg=OU=JnD&=WXlrEC(CNftP^J}j9Psw zM#9ljM)qE~gx;#PPTdkp;n>a|r&*!Q!UkJEUyu`|4FuERco3WMR?a%(QC03hI$sqS zQqyuH87DXXRA{hxkL%xlfMncc6b@D13iM=uc@+)Q@My{EaEk`at_;#4S^Q8ql3joF zc*L*aFMo$L*#POzihdeqppZYq$H4boP*RIMnP>Hj_Mp==7Sm|1$UJ=C2We*xY+wj$ zqw7@Lcwo^!zP!ch8EAXfYAqV1bJPO%!2@gkzK^r|$tLMs5NBP~bWElqA#Hb+a3C{E z6$hVK53hkor#qfmXQkBfs2WN+V^xuKILfOZGF>wlgrS@_<)p~ha*5J@WB+;i>^sXL z=P}`4de6~1K(*jeUS6y*BWRoD%Hb!yvy|D=4UO5;I#l*lOHbaQ1xJm?--gdcDA z8+$GFh!EZhlu6(1b`%CAVgDLZa}JpOrT9XhSMQe#(IH9n1ebtP&bmS+)lCcEEc%0i z?;`r z1k#T`)HVL@^rg3><8V5;2|xd^yH#5mNFMfNaVrneC{GnIaA`Y!+cq~wQLa6gt~u?U zW4@g+_S`MHTfZIP6)M9~6?77qSOK-CzC(bVoH%_E?8K%X5qrM-w~^B*8GKS3z(BX1h;n2QrHN#)=Ac$9DYCvO0Z^Bzs+p5atzC#>Qk4FNI4nn7X}@s=rc^8bgVF%Y z$zN2fjD1X^q?lFD=*42{`J8MVB1-riSeQ*e()z^8T@vtlxN;J94%7aS@^D`MD)tPE zAAicIT}Ia^WWDd$Li9P%%n_&(-ZN*;AIn%rc~@ZScFq35LWJ4e#8qMs<*C9yPEtSJ zR-eI#E{g~wZGs``85BMK>~IscNN=&zDbmd#OY!93*y=}_pp0!JpMVHrGE}%*E|>cU zwU`hZ_Hf!_xoGNO+cX9*y^RRe@CeoUKGZMk-S~sn;$V0lroY1=cn!EXf2it0Z~<1! zBD3s9Anvqd8un8mN@xbl?ks1<@rHD?EgeQ%w4!eMoYlcK8`78eZqX(~t5Xan-5vK&c7RG^ugDc($eNV0s zSfg2vv}mL`bLg4@Z)a>Rs2^s1N+;?g=@?FXDp-d?kwr*~y*@k<_v|Pf5*k+38oK#C z<^0~{JM!V%7rRRS;kc`;1f+fFuhj3!SHEOC(FzYfhPrsB^^ft=Mww54?Lvyr(qd17 zNtq8HB|N@u!D~?(Y9CsI&0YoTyWu}})&im1FE+8&;G85duPU#W7x2hjU64(B>#Sng z(oy@Mh(;H$+-Ubl7yQN68%g1;pYc8m1h=sdws+{Pt8=a`1|nlzM^?CD=Gd!<5C<61)rC-M^< z;UZ`V8p) zW_8%13?yhC#;1s~DrGDtBy|bS`tA^VYcdakRFd1XJ054X*X_V{#9brumO>X*C<|KS zh*SMtH`0+om;iM{<(H{yq6EF+tlt)c)SQ>@x6Q&PBYq01bS75-lrub|T^E{EGZCiBD(C{HKq1v=8c7WYo9dm+ zQG}lVSXV9c(2dG;wyT?))&NL3J2t+2f*bIJ1+klGA52a@Gs!yt znU#sy(ke5r&2bnBIT5{5O-PIj*y!2zQH=u5N{|9Ow<#ClBjD!8bn$Fi(GGKocm@H6 zrk+bYpqsK=!3_C>=Q3vf*z+O?zYVcYpbQp0`4kD{rN7nv^~^k5@ckKfXMc&xM>?1I zPcxTDt2obtxD6vaa3a-Ik#!h5uvfdT44;Cq7n|V zci7)NPPs8}sJQPrQkM}gl|;IdT8M`_3Z#xrQ8124pVC(>!t#f8hAfJiOhvyZKH^fR zzRvUI9WEXeN1OAIZb6C~c-YNa`I&#&c1TgId_L;^Xsm-w-1#ojDq4q1Qy7DfVaJVs zzblJ%!QDFu6gX$c-h~wdNdUYoacMj?ZO~4#^@d_oVg}0e*6C`$lj(iCY?$LZ@a%*i zE!KLQXJ%Bib2^CZ>ghjPHJ{bDl?2~PDUDB7T1lBmW14Do1+d^^?a12j!fqqTwhZhc zF2@9MI6^TsBXoRxr%->)wvHFS@pW;pCp&Di@)fJjL!0HNwB8HW$&bSoUXA`S{Phj~ z1pP_3eQ$6F^r3T06-fK}u1j^`wvg#wQ@}lo^lMZ=I&g!=OiZ@#P-y39i0PJYSM~Nh z4_I=_@~KNZz&t(=R?#O*(N7&ZBX0hcoKWajZTA!LW_V*maq1Y|LKaVxk-)IrT2V$rv5Fj8n zz@zQI$+DPz0NkJzm_?u%KT>KFDxC8P1fSw2rV=(`p2QQNBFs?iz% z0aBv6^tHVPj$A%goCPpI+wOQW33XyI6Rr_$k};4}$O~Fqgh~BBwLE!%wvvJ*jlt}f zO?t6K4>j1eyvDchhsBd=TnxY!{8L)xz|nkCH4LDYc?JQdQTTdwduWpVt9c#t;75vR z+6?q&+EjrU<&O!xfY;!BHzoZMhd1XTy&Z^XMH$PE6>U~5_UwgI$L%lbzm)t88VoX< zMB=vDq{HRhAsR~b$Yq8ZK3|ozNE&%13P8#2a3cGu$7o3{^T3t-yUT?TIeh)1xy*G~ z{Qy4k<(rB4u~}Lk$!`$!BaPdn2vcqmbMYqs{-Mc~&7UhhHkV<8Vnp<+(?VjNP=F)Z zt)0(W1KnNc>2Vdd3R1k6r9PsYcv4d*P-KZ{)WE9}LZES5!VC&Hk|}>5$?JB+I#CL^ z<6<);kfZ(s<`?^3$?q9_eTGe1qIZYp;_WV#r|9LS_+v5<>cvSIJ;%!=Kxu^6xXU z>%wLjV|;vFbqDZq?`t72bUAamMpk`#5vaxbv)F^m*TJV;5K(Eo^>Ik%JAIXMs-Zb( zH){9P?a-Stry7AWn2FKYvPPd(6TvhDGIZ9pb8$LIa-Tn%ShsHH!Pnu7*`;ApoA5Z+ z;FLhz*nx7udnpm9L`+`THn(0rUeiB8KZM|j{*XVz{ObwYxSg^;0&sN(VE$Xw>_1MB zk{y7(r*HMQ`|R(_@e+Web07-f=!}2SE9wF`I=|)$!Ly;D+OpX5B-&77aXhnVs=9l& zoCBQCzqZr0H=uhf9*Y&-`*dc!nC_+~ThFX6ZCqLPaL62k=>7^O$xqHvq5n}V6o(Er zs6iQ(y~uHj+7kwjN2yP%VduEl)jRnu80$DD263KI4y+d%yn~;3buFNepqM$$Z=nuR zk}N7~eR=V+7qu#^+waPh?~m1mCfH@@&qO-0;-~CdybcHVUu4Gn9Qn#Q4za zTJH|Jw~AEYNGtY>I1TY zEF8PNytbeZ*yxd8>wcfzQI0Tea`$Wo~gavCgA~MlHB5i`s;Z}Em0>l zZeD|0U=t;ZfmHMysYB~JIn079vAnD@2wYbSQs!8CxZgu2RZI`a(*?}X?A9dut(t>4 zC;LqjdJYl~+N*C8lR@4rp?z{F()k3LKzv!>sDyAucaD5vXw@ksP&Q2rFArv=Fltt=)I1u^57uoO+U^BIg8 z10Bz8m2c!t3B~CL=-qX=Q7EMh76@+Imi?(&pF%4kn)JVfmOGt+UbBG;b}gs>q%D~U zfqaowlPFl3P)2^{Mt_5kl!tw&bpK}lvSY36vt%<_5lr`V++E|ma%OR>+v*U6wZ-St zSDeb-yHh@={wyMjF`wDId~p`NzH13Ol8zJmv3_G4b$*RI!r3P`o#fVaLGZF~+nut{ zf}vE6_Nto&`y^)+r277!y@mhtB|-W>JqHZTZJZp8jsNo#|G2gMZa4Mw0l1yivHsgs zsDISle||#nAD{ZW>=ysDR>A=ooMh3y@((H{p~b{Oa_M^f%2B{@i|g#;*i-ppT*S;S zxA2TjG30Ch?A)B?tg#U$$V(L0xs-O6>gHZ=Z)NXIY~guGG!~cSnkr?iCCgl1Wt0?C zGGmEF5M`2ZQbkRT{x~&t4pj?xkmaIEsX+8etU=B`ulhwT6Ui8f##bJ6DR~l+j&}q= zZX9x!^v`EYN>|wh7ic<`GQ~SoUiE1DV|%eHA3F67M)a6G@(#$&O$)QTQsbqA`!rkv zH%5jrb!wa(yQs_GSjEDN6@tFjwlguvSSv=?A;Wm#IJ;jhNElh_b8P1O5Wt$LmoFQ3?VLXq4l~*E{ zxF)B@#)R$rES)>NvT}gXI3t$#BL%$_J04vedU%RHk0PaC#@a^uRG4~e}Z?=}K zN$y%ifHB)irof3A#?S zbH4OGHo2GRC?2W|KmoX-XT`9`u%wWxmS>_Y3>r1-aiyajV@GKy8bxxi9shDR6}m-c zksW(y7|rF$)qcq^O$Cb*pr8>B_lWCuV?UfNb_7uF#D>ynKBvmtK4zn5`8Q98Bj_9e- zgcF*VDWD6+!Av?H(6eDwy)<(*b%h)=y$k388iV*Q4Pi*K_lk_=+jMkW71C>6ni#M* zb-XbhF!*{14esx8w#(hQ0R2t+G?cDeXc!AmjCGm3lq^%=?I5390m0wP-&LzJ5f&Rr zCLCJl(w*64m3K83j;P-_X5kxETz`ZQt;*zf++->Aw+wGE?wdbp-`5pxe9Q|-i!H8T zwo*5!T6Hsq?b^PG*Eo8C-i?1@DKZ z#0~>9Gs`q;n^^?#R3$%LP~kDc)lfC33`^8{oqnM2yCi9O`>v4Bd@FL5Hg=dj(poO&mMfuICVt#YLD3$GEg3t3h&qFy0PBXfe}3 zD^2J%7BaFT1v0aR0n#I0p1>p)@sGAQ#M0c!(fVkInxK_eb<+x4@H^@br5|A3HrVIBRI41AUb^|sw!@49 z^W}1NTiIWGN!*u6&(ln5$AkfR(dc`}75GKyvsLX$4IevBHbOR$Lp0~Z?mwZOCCkUA zz0K|6sZ(ZCQ=)Wka19P#Lf++Om+Z$xqmeb5?i4*9G?$A!PKjH8Rc3Brrb$#4En{_G z)#an1-pDL(aGfJpgjxrk=ab>W4?$Wiz_HI`a_kH37vA)I}si6%d|HN%} zfIR3=DhwVE%j&wd&8&=0bovF8m|_lNg5?0F&LftnuTBAj5R9%Dk5FU|L4!I%=bS!K z-L;mqJ^@6m)|z)x2$rm+V>Z-w{+_kUiSGtYPe%8?SGfC#)lP)=$5Y=yl$fq|4&Qu_ zw7kXXI17|b{;fR}Eq0n-VzG9eZfA5tXq+|!gcQf=7Xa8Sm# zy$u>fqcQ(O;mTVO?)&wj0VGJ;6KGTZ+Dx1r1S_-sN`tk%LvD1NMFCYZ32mr%2gK7@ z&NcRmAXj0oyG)-DBRN345X8f@RR$YXsV!NIjXa#89DgTK7MbE2cdJ?cHSdv7L`V+=&P$#9P!|KL9eX3ff$xF&0 z!Zf9CXXcM;uw-2zwj89U!Sa0XxkFzk&(@;+svzuDi*^0g&CHT5J)$|f9ZfLo?5dLukpwC6N!r-29CXFUdIXjM*B%wM%4`B< z+!Sjkb0sN-MEM_NY1RnUx4ucOUz)Kz%{<(TkG(g|C%`0!3PE~SJD%4ZPkU=y?q4ph zklTXnBG0(+gv#Z}77aoA!+&QtDGKTt0kWHhZ_}sDr2c9XXER=WxF>v*me*Jp#583z zu^9p@LuGLUEDHEcwGCrd1z@3Yo1g~t1jBwK z!!FazE_&sRVQD1G41i9t!ZEUe|JZZ)JU*cU0|DI@8MNqCv*u#BeYiv9U8QClzhrWV zV^R_nFx#5=?P#a&EmUznDyH~WO}e6usML-7@hB_|qg{!XDBcJB_tHD=PuOOAV{^!A zMg#vfpyK@DDy95#i0Nk8#nkTU>?Cc<_6d9zfb{XYl<#H;)BeewiD6QxCVyK$t`+XVWO1v_~o(BVZX6;}%hX z36rWsTWJCg5tTt&1D4_$anW(q2V&7gZdHZ6!+vt&0#!TSu3_9bt6t4kn1Ux?MeB(A zQWvl{7C0RHJD#9ZAOu^0i1LA#I0AeI9J>M_zG>nIf8&-#^P?u@6arA|fDP$Q73wtU z(Ra!^Vi8B|H9jh)r!__Mq`zSgRpcQ4#&9FiqQsKQkCBR8(Dih%uX6cWNQp~9~! zX!hu1B24?UFlQH6m$T`K133C6voPi@2V{%tkF-QEHVo;-vlyjeHomEeNo^zd6Zp6s z|$CT27qMV!7&T*4}mk zyAnItsk}AHWvlv@&w4yP9_g1!^ou$z2Tf}8MX9P0@#GjrmRSDrz+Z*csvCNy5hLv@ z6R=yj;O!o-S5>yJsE22F%)`4yzjE;l?stUCqSzRyb_o#5(Tm8;=-R80sI(DT7v|r% zjmOY&Qc&-qC%Eu8&OkL+YwHL~U@<7W!v#gu0!IzJ@~M(y9vG9{$4F~)omx2Ldw4mC z9ts|oviUiimJ{*av`2Sy_bN3rvsMLpaZkv zN>ojsVNrZD9bPz>l7G*@P8N#OS^nrhLb30{4q&tG`ivb({#*%_Y6TZ3=r7gmTiHEx z?mJ^KSNW9R2xZ<4!i&Ox1zKydGM1S&=ao!boKtnzW6_j zl9aLgKkCuHnkzNMd?XrOm_Q~aG8eUVU^mp`Vw;3DwI~!xDXDd4zk0al6OLG~W=ZUf z{#@^E0j!nIZnNIbZa}qqIS$XvxqVtE8s|$8Cc$>NLEsSeDG4L8X5b#sxvSWzB#kMW zc9f3Wx<>7XSZeU8>@+aR^smDtr&&0n)9A2YT2KQhcG%0(5kY%Mu&M@A$ztmE-bRj1 zqI{c1dhNu95V4Y}-w3>&0DfYjKD^$x{)_}O(Lm>o zT6fAZ@KB_IWx^SEGG~XZOsz2rLC`OR2}|!UL($JhiVT3V|D`;?G)!#7i8KL6YtX;> zyotw_PFip}aWsXeWnVP@i&RZKC`h6{j_C1Pjp%rs2pru(qnXm7an)@G=9@_i9K;W1 z2FX1Hf0#{#liK!rs5|JzyF4sce&iIZ$8=ghf|tsZ6h$>VblDZWN1^%ut> zqvL*?NDaPpgKY|V`~7}!wS_CnhG55+AuGmq7fPtnseJHbMADPy9K(M$4M32!ruRay z|IqjNMDi9jJ*fQs?%&XcIb+K^X<}#36^tY;y72P+er>@VJhtWJ!1aa*Jjq^r3P0E* z>@M?!EWi|9MYhR6coh=_Nz*>WZwx}I=}6-}SU*x;) z{=Z0j=l@RoWy`x`+qSKat&VMUY}@JBww-ir+qP}n>2SXHzRx~0d!OeuGiRQE;QH;V z&#J1m-UZf@5q0)JUp5dV1YbwzQeiJ^_$+QM#?LSj`4fTSbB$n;CSbppyJcZTer)<{ zLZ7?}2$kC!G@#z`{murSB8B4dh*ODH-xH!4oTBsTwmqR>+r!r5(Ly76HeIMrMFcR^ zTf4%?KcMw7qKOVO`ib+gZ$)aynv)+SkGgVmL-=k0VzKXYao?&3#VLBtmYk9UW!mZc ztP<)9>U%3QGa?i=4ThXS2=-fY1#7_=7A6aE^X0&=V|@i|Du`IFRLUlr^e5K{8@R-) z&^QM*vT__fLBHr3yhzmFUC3DyoCqzS%-mB7bSa8y1Qpt(L1 z+nbm3!m4i4=LN&c?MhSPCPE#e-Ii$6kTxdqH14uJG{ei>>o|9rTDQwW&^5$0^`s~Nq@_>j{oYt+GByjUIVLS#;cAEUqhv?O~B`qYBd3@I3 z_;rwk!$2!lP=7}FlV?HcEJdIuXXXfNTb_5L9C7JlcyVPZ>f-DYaJXjbyA17opl01- z@Nszm2Ha@N_o9kfdMBZNdb>_jP4Qqr`eeRnsx+K@0!y#nhVCZlo9Wy|TPrrHpQIEm zb3Jtjvg`@R!t^)Vs?ikGP-8CqL7@TQ9%JkJ-d{ z-SA$vv8}k*$M*qcN^+YXf+!!)`wiZV4YK&nMHjIuO{$=1VeNuR$ikHl)x~4Ezo8cI z^h<4Uh+DixHToJ1@`+1bwIIy*-%q}av+Vj_0;~cwfavA_=62t|4)Hp#Y>su+tDxQaMS8Gx+8r3kj<_Y}Ta%@=3U}*3z zH5F2wYxOzKFZk#@8vo#RgX*UWneGRIebkoX)#xQeN)BM`AzV3LiAG$aB#Q=~pd>>y8Kt z)yp;Bn;4M@QF4+CSD4sCbLWcYgeVwx88z7QOOhotg074XtgXc)1NcUZ^NXXi0@I=g z+(a`@7-O=olZRP^1!nj5UeW0}h0yxJ)F1-`GmJ<@PgWR2N0I~~VJFO&rI9jb$LzBc zm_Te{GcAP@C$7I?xMiRHyjhV~hYxTmo*lRP6`eOk>yBgy8`>CMTrEI+Vn2j+0a|dw z37nUCLKQqGnu)6mO{G6T#N?GZ5}e?-aMQ^C{&465xp~h0vUd60zTDpquee@1q!ba`>%7N7~+FW|06BJQ_tUac5^-7|Qf^vN{yUu13Ry1dyr zu6O@o=79MIo@r`V(qj|A6XF>uWQwg2)nY7^4jjl0+bSq*464+$gZUb3=pa@Fc~t&| zsZzQ);2tdv)`Evz>sK~(066X!w6JUtID32$=6=JEm}pHn zj@cpPLlul%=M|e~fY_^C{?;5|bG7Au)FL0+J24K=wq$O$ePr~ouE=7;zxZOmnpv}!=vlz35OYlDT+M0At zpE9l|NS-#4Pq@IsWpi~sso^$Fj*FL8iSl7ALs-K~;2$S%q;BeK>x+;eH|&DJ>ob-s zHQN~7_J_H#DncQGfmBcE! z)ZR|?p3q-GcMwH2@@rMXC*Sjs)w zJGNu<>k)Ip@Ijz#4JPj>%o~Pb2hFarr<>s$BJ;#DOXH7c1v|!F^kr=Wirt=Vr&WtMX}_ebfa`XmTV)ImUpA*uu>Qazn<6+~^zf7&T9oqeFw%xM?Z)Rg z4o6DaTG;5O&Ef;nYWIq)bN1J`~!ZlceI;p*8 z69h&BT@vVYY&n7^g7$7un|t0kg7q~^l_Q3&FJ{Df2w26A3Mv+ICAn1`=6#u=9vH8b zULO9=y+=cNuQlwem&U*=JlZj#+t2N2MJ`hv=Bc=-g_s~U>)TD<7@e#*AmTCUIXps5 zHIE2@*Xh%gigxX01ew~i^BA<^@b3CdmsuuYw=9>wfR6AeGIHE6lT4JWnPC>x@~Niw z2#}VBpYxFT0CC2uZ5RgnUemSDb|uR^I&xUhP85jjmNonR8ISOWwuHV!30<6F=o9of z@W(!suB#g!u|`85ao=cP{r6?&2x65<#{Hdb)%isGtsZ=5H))Im{6rTEm80P4a#*qf zESjlJc-9--hE8THy9V-%&izx!0cK~>M%4$yMf`^IGR1j+<~s)rI^5FV zBebq{_TMFL%6jE1I(AP_WR!$iRB#3LZHi{Wv8F6LeQSrIW`NnCVV+pyEMKYOvNrhVtymUs~+We4&^-aY3qV@3L?dI z1XG6Qs&a^)A{;-Z;>Xy}vD>(3^JCWUdOPCj3Z=h7r^?-mvBf}KnFe>={|xedasL{h z(hdyhTLG)qASJQEyKI_A&T=8Vjlh)I9l*hZz5}&)v z>Nqxu>y~Oa{ve|iq1|u`F(G-nN1?vo(%b}Cf_)?UwJhTR`bBGHf{t>)k3`!T|YkhsmA6X5LO*OPhZA|vg zlBlv?=IYYEBoD`3N^$F3vF*J{(Ce5-E@R$feBnbmkxkNfzpDiMU?jA*DX1I^l;5U1 z#c!tnjf=b2FJr+DU>0bj{x=3Ge?6Q+`i}pB(^p%!!C^=AX5aBm%@v>_yVI;YC$G#T ztg|ek#$B&DKo^XIut=#(wM&5>P0A<#e4dKk$RLI21bz%4qdWFEny_eb(ElY^QFu^`Mh(Iil(Py zO&00H1Z6SBMq&(-?mH561f>oJLoV7k0~WwtyOUP$Fua(ZR=|3;O9Y5rK(V;xlo|*B z)B$uajx5Kuse5X5cyVnk_GL#jbXO+mnkCj8CoY~h5$d6!Vv&xF# z*qAfaBWPH!9z;?&LSszG5bj4~RNi0jXWL99K4`%hCr}*cJPU16IR{w>s$yTDu+0l= z3Z5xQ(eCo@OG)`U6D<99Pjx^>M(^Q^3_;PQ^e)w~B)8T8hF>ug_at+B-%VE7vMGBB zKY$Hm>Coku$rvOiuw3prJ7qxWq&AmjGt;mN=bWVxArS*@TRQg6T=s2Rcq=YVWO zi8Y;Cgl#t&%A6W;OK%hooEjyy7HoKK7(T(?F$Y>`MFSx3b?l1XGaoDm*P) zBj%iE!Pb@(xhf|(tiMkL#ljECFGWltD$GU-o*Z*35ry0+D>E8fs{ z+}MeySr}mvuuMM!F4gsOhMqzcBR`uSB0aBoeHL*N<>28FvU_QQ0>`w!VrWeaEQ1XI z;R`#5aQ^J5-IGNHcegR--MYW&AU^F)<|^Jcq-y>gQU6J&#S4- z!RuALVUaoKJl7==V6A&YRwOL2rR~ktO~E^WArDWs4ie0mRRFn)KKAy5E~Mn}W!(k4 zVJrc{zh^XA%yiXX%8HnxJEEXDy%Qxk(q3tCmPH9lR_%ftJV#eDt}_IM;LBTdDMj@8 zIllx9Is3%QzcYdi4xC|j1#0z6L>H*zrvM83#t|1w7YS6c|1NosJY?5;3JY1QwtIdk z;Z^W=>RrZO1$?jo;e40itfH1vy*Iae&rZ88IRc^|hH<`38a=hP;#<41{$_9ii*sHO zaQW!O15iQV)BYStb9pN%1s|VO5lj`&P?Ja_O=3*Lz?^fftE`A~_{bOGD0?MGDY-ga z&t_X1$MJE9gi>>Gaq1#W6YWb;nlD>CFH50J?0!Uiq#vFJ&JT`;;wzRF{plSCGe7Aw zJy{f_?^JyB_0&>b@N_{y*aEN4BL<-1l>-*b=}TKCsUz?clQr7&68D=)fshMxMc)b*HM=@4(8^kv4%EjZ)pmj z*^AyB9H&sFY0F%yR6GuW(jqQw)@w@1eKySlTpO7b7{R+fvGFTaYH4-b3ByW6f0L;? z60lDthO|boa2eCA!GBAOV-IgWZiZYNG5@BVVbHR=;YKcm_CN!|@LEu4nItTDVH5Sd zSbqXjqhhv5bY`1=WzDqt)?R$gZd-#2l;4aRP&VfX#O|Kt(K2Hg^~>^Ic((2}kQCwB z(SANp)R!%Pr`W<>MxOhRcu1CY801~|ErWq4KH&<*~ z^8wFIZ-ThfPOzG5QdBa6ckK0iP!Gqrx4L{CJjl{#$sAUR9F74?CGf1?LpS>~qoAcm z2WJ#0jht|?Q8CS>4bBptD3F9MRLG&lU|mJ07)zCOqH&5P`0YnIu+c~UHv-RYAHLbl z&T8J)Z68OyA1C%vBnx-g%els4XZIc|)z$4;S&@fRFgkn|8~E00+bNgxJGy8;g{o8vH#g$T5Yyj;oS{@#mfUa~ z2m~#MCQhYCMDAfzVyAO%MJEH33O~Tw;TqF-eTBx$Vu^Up9zL7MaxDTO3f9%j@xf?= zI7#^S!qq92TA*^)H7vc6hJw&AP6>4$giN6;a3XXQ)=P zzFhyCrIvc+3eySDzs|z{H~LpaTU)2UXNLe7JvKY)KVbCMukv=cz%HAG#ym!@Z|3o0%aV0s!JC;K zk+UVB!8sg~Qc3w6Bos5djF~YTdXS*2O9uUg1%f`lT(rs_OIv`KiXUo~1?3L|L=;W@ zqv(w9gsYg-Vp$pxOC>>tAk%*bEo_a8qid$)4!0y?64`h8gzFra_c%64Q9AO>K8A}LF@iArzy27ZoAIB3D=yI|Gc ztP&N6+bSRrkl1Q{B?ekc>5v<7A zdiF1~y9%G_ZqKjVbZ>X!Z@Jd2%fM|(I<37KbWyg5Ij7Co1U+;{l-GZrTCOU zk;ZEn+!4EdyWINyd1B(_5%G4|rCF1cgU3x213Ylf=Mw@xsRd5FmL(0@$ss&MO_-~V zqf9p&nhv=Zt;NM|p^Tu5YwfG?QL0??KWu@0z0RlC)O<*x#?mHG9dj^$$j8Fiv$OdwPmsO9mE-Pxj|*eZ?BI{z1jQWO9a8_y ze$rY~fV^HO%EdRLVm@`^&JUOVcD{L-^uwhfELtNM5J7`-O{5!4lSgJG;y6-1BP{=1 zRr+bC+m|9p?VX_!_f6vouaP*~0`MUAbbxDVI~|yC=@ogukzy#_m^BleEU@uVc~B`m zEa~_VI;7dFhf|ROE@Kylh4$t+)#}#`!Ow&4tP%|+hESW@*?HYBWjFF1kmn6YqKST0 zjWM>Rs`tWN^goQpP-SKb(|K*5nsuv?PgvH4T~|LwK2n{cg>UXnUwhS1{E*0iEFOQ` z88?S2Y@g`$5TUJ#m@EUqeTMEy8(2+wMKj;_VrxBUH@_o!-PE`|I}V^qKQ8@2tCQt-6O1D3Aqe@ zTtjS&kP|zrO;njFmiR8t;PZ(t#~iV7hz7l7m)}>!gHAzd!;V$2Jh2zTNP6XAvoN>0_5Tdu9 z)t{wSYA56pjl=LmWAYoCVe~ft6r##}dY>a50*ACp=z4#^A|@6c)8I?&RcGom^*aNc zg4?|+8R0HD5A1HzT;uEa&W}1bN(qTYzYCT#t4tV`0YyD2g-1Bx?-G}stKaqy9Kl1! z^RgRnUz@xSc+!$DrQoYlZc*7$d%|<5l5S+` zgxfiwNSm_+PX$J0)V*H&(4#a0S}NV-oLK2y>d;87P1$Qjq10D5L(x=OqfMc86tzm8 zm7To}k0VXj7Pvo|^u`LM#1Ge*Ttd zj;we=;HSQgv=~Z8^2i{2;>fxL;Wej*C zfK^kaS-`4Ti}O!lR2rz^N!Vd%-5=9JxTlW~_BN-?Tlk>mv>G*1O$2(Jc& z`Q}6t01Vy1&5{v2hlM%lw=Q8)yuFK?;az5N`jYJQ(jNgQfpnjij?bsTa!YJ}Um1P? zR(xo>`e%+{o2L4Og^{L{eJAGcVAmq*GP(&2v~B*bwik5Mp`Snb+BV9l3XAQjfJ&BT?04mbyk}o3qhbuXw7(wrpqAO7oy} z7z|$dXPi-oQYmv2Y%lM@N@J584pI~TI)X5ufc?TVB5+k`9?TMmJULPehYYP&(b!ns zPq2+MNp_kO85kJaoPo6&*;aG5_-@0d5ZZK11yRkoZO3~g19oq4iIhA;dDFhdR-pUW71kG_&El*7ycV3?q`Z1rI0t0>>-w zCkOz($MDFq-Q{3ODfnat$6k4D?FyGelsk=UjjmRlq;o*%VGiL$5>ml2g zDz8X2sDvn)A*dD_W}0VQ8Il*-NOBhQG9$?aE1wwpkQ#Bx<`;+fWhAJuDz{1IYvL^&M6%=TpwNw-ZYc)*~smiI@|#K0TX=*jLaC_4dEJfO4jnsFtyh^MGY>vBt(Sv>~hbD@@;<4$i>F3ynM^An3+otOQsH}V7)aeZkS3luL^Pa zS)7%I0T}~!_R1x1X5QLN&)L1!nXp_E&Z6I{x=VmE+hn6b!FK7W;7TOhP*ypE6HihV zSGYB%s+pf?n`Y3=x8yxWE8InSmqnY@qZ05()HI931(5=Dq*4u7QwP*70y_TOSNFWG z*UuRS0)H9rh;`cTC*!fc01)}@rKiq#xA&4Aaq4~(OVsJ>I|GZ$uwckKKM!EATiH1e<6qe0Q4UuT2fo5)MQO6&D`P)^vbXe`+ zBS-M2{3vo7){vgc(R(s4R!Y-shDyXAdD3C|%Uf4tbnm)q=KZcy^`Qt2 zZ7@^lE9hAKeiLlZlsTAQ${5A$=!TU-;m)pxqeZ{wF#uV*Z>{xM;la5`9akCj z+rtV~VjSS~`D7NOtcbwYO$m8|Z+Jy<-s$r|iM=7sxaNl6P>1y++!?PpCYdd9{bXJ0 zJ{Y<2qnu)Pj5BGSffW2rhOQlW5Y*jDr;AhXNN$2va(1lC&4pR!sR+c^`{9wL86;XB z7qbzAT*Ph~A`(yNZ^s6l*YxYp?HyY1mC76f@%0CPFH|T&s0rkB;C0tQ9;y#}^xFVl z`0+k*#9k#x=JYE#|8HcEo6;(tr`ruIbDc!TUjNo9rHs>RBWgbWxMMrk_j}03ieysY z-D~i!cIMV|U62;RE6EO-n#(1aoC|bRZI56j6yFgw^q(xbOUzEq^AXJrsXRzkj{P_i zo&nL6?n>g_cxmO}sm2wcYfOE8&8)>gYdlUN+`nZvJTqnzKr|i65yyS39tKT*5H2Ui zt-E&4e=5Eyv?z^KYNECiUeZ(-{qlTWp=h*Ep%g==5YvwW8V}*{2f?4_#f|0UvTzRX zmSe@m>nsg;J<6$TAuNrG4nw5vdwMhMm_}-cT~~?WFDM8|-8sxHf*Sn8_Au73;NYtF-WLf;v)l z=)#z!(XTxQvUj*rHVi)=4Sh(Vn)0Xar$epwUDtY`obGPr^*4i_-F1YmbBL*>izmNv zBW!z2DZFw+LD@dwclycNnNuwn-mZ$qZ8d*rcgF+$;%u$CjUQF|?b22vkbx>NQY4LEfA?XvAI=3NLz6IWR2OQWP;WrU`?|T%%)d}wt#F2qS)tS zg>S9YQ)gxkD%GTQf1Vm0W%p>yM%X2H(0#Z9EJ%VL2YK}{>3IyY#GPV%NOf=7@B0LN z?s@Azj&x<(Fvq>LsCK_c!l!wKBF)@`xV0sRHSm%MOxjoC%?r%|_wd4SOPJxT#S{R& z;Zg@naT~cUK)Ur@_*2q~tD=7vAi4&X^8Z@kNL}5VR)K&Ue3wgKKrkd|)8)ST_}rS5 zX|dRd5`ggpu=X8L_`bfSpi}2SjlHUo$t;|E4WF>HH79b`U^ouWUd}^hH8J4brzm!K z)ZI|{1-|st*Q-zW4~lta?#fjvsgq+_)o*Z8P+Op`_O}jOc4jlR1#V#6le_g!I5rgu zdlHr5og%h7n@;Y}%h^|-9M!rTiF;#y#XTv7H%jUzvq9?vX z8*Fr62~R+lHhLAu(f3tvj|6{ZkeVTg?mgG)QSRL&^*^>TT#q~5_*D~GjRJj-Y-=MO z`9kPN&h9Jwpkxzs?v4WaGn9oQ!4rqebt|~dRq@Au4Hs{3627_)K9HzP0$!3V3N>Sr zxw&@P_#lj;2{i;b0V*j9nTS=+acVE5R z)?HDaVcP*P5JH%cl`!_#&>9lcV5(z z5dMpvuD%$KU)?vyKmV!c{|-yFv{vl@<)=tR`roo#{`OP+t>^zmjte=PTN(Z5sl;o5 zy8`7?+T?|xX+CX=GqpcBrP*A9%iiqA3F6LRah7y2!k%ms+El#b+-@7z=d-IT+2$W@ zVH|Rl^*IY)p0^8&#t*Bv)vfzW(;HkdC8+MGU`A#~rPNX6;3Nm+L@=NgY)?jk3`NL5B^$ z58Y~n?sCth{uxxXc7=%OVocpU8z}{PsyTZqGIbUg7=k?FyxDVY4YjiM@Nf2n zPF<#wON=b7*bL@KAjxFIS4N!mpr{{iu0C+4$a&PeSqqRNKq-sXdCYu1#muF2LVEhL z9RPhOFs zK|3QU=EZx>45kFlRe1-M9fqiOf~cWelH@fwtHSBe1=w`iDZ+9;jZ#{Wf+yI9Mxn6%LWM)QBZNrDyIoN}N zwBTw-$;MPqgR9xOdpo-`d1U~@7DWOWUF?Fo{k&pDECIid=Acwozyo7a9U`L9Ii&|X z2Nq0`N7EdUCQ`Oj7yC}2#G^y7?P3I?1aX#wL?y_AA3n9ZG~^QQ=`+u0FZ*`o7~_@A z>@)zru8W9=o!sRh_93qLcgAlgm(On>o1cQ$;=j&!bK$Q^!K+ve7-8C20-V!Zb1a!& z*X3C{uIPNW2^MJHAl?{uvIK?9tG@F3e{6*3PXGYcZjO=sx{*6KoBEyZv<)Mo<92nv zE9bZ!92+0D*ZiFx&b8`=&j;~Q_(f@Hvrk)3^{pdU10n{5_0D<_g}Z} zs~pjKdFxPmLlD3+_nEm%>+-p^gcYN67cZn81S1>a@Z+oge&c%b_jxrO7@W`cbYAbv zkVIRk?uu9YCOE@oBocgJmtzx~T(SmN!;vzI)dXx*?Tm-~DfuNBn}4R$M>FHlOGBEZ z`IsWj#V)QJ+xNo`0d?##_Ty%bKnkXm!X})Cn>*__`ui4zbHJR0Ki^0gEw>fEzH``_ z6Jx=ZtAer_>5AJ8L6MV6X(#xW`wA*SNI)4TR0gY^0gt1pS^z9Gj{e5Ae^q#XecARg z8Y7HaFUS*nQ-ru9EYUdWK#HC>ZF$xnD#-CY4#;WW;H7Ctb8F9GPUXezti})lpJ%4U zt4_cfOJtLzt0Y;V|Lh7QtwD*QG?&~#n*t&-eEZB#IS&{dCt8k0&8HpqvsjYLoEngi z3>&;F1?8)KY0+$odMg&=j*)pzxPiM5Mf0G^thqrz9_&Iw#+B@q_#8+No%q`o|8}|) z{&~K`0i~(QaAO@&gXI?=eaKoRykfo;;5(S!?(O!Xr%DaYdTr`AB=PF3V=$8($_9N@X7Gl){6OPZwIs+IF7_aZ&WcDhzAf z0*OMh*28(KbhM6{-)}%Fy~@aJ6j|w@+rKR5G>?O~pq2-gR6D!s_xrsS9c{Fy}W{xZe3`rUB z_4#jW9g$cTUU5K7r2vpS7W#jfYRS16J6P%42^uTOliBQHo<9oFxO~RybK+$QL!| z#B=bbPqvg(Z^68sR{UU|5e-$WS%0FVM-NjV!i5mMkE-#6h=i?`)H=(+P|Mx4nt1F} z>zIi!TIerJ??Zks3yzc+<2oL);H$h-OX}HQKuWaY z$P5d^M7)nh!iG{IPPkX~$DHYp$+S13-`~F!09dp+HCaJ!2k{;Z02ZxZvmL46$)})s zcokqw37hce(|!l1Y7-0)?`58A!J50j*C(h-tev{22gelC$IK2o?vME8EhH^bo8Y>b@Q8%}Yn zwduVx_eE-;yOw`*(n*B_Je)r~$?PxgR#iVsGY%MER#X;QE4LdM3h=QvR!SDQUqgWP z8138@mh-gpas`V9D?gl$Vwcku@7%k4@-D}mR0WDu#5OPVs@Bcc42(rX5I?_#omYOm z7&(p5aUC4tLaoiIL69*WBBN-(qML{18HRMbuJhg2kDO%vkPF6uZ>tpB7K`$*$(SE= zAWT2ztR&uoWo0{w=jQZIVO@|Iz`X}A5q(o;nv`TrHpgl57U*281hc({w=PaH+-g{5 zXrRuU=mW8v{R)Y|Zt*j%G*3^;-?bKw{%IjQ{jBrWr1G%)=-l9nm&GB!t+zvL&UL}J zi#L>=8|5*@&2@rz=_#J&nsSam1Jvz{Ft+|Fmba9EYaGnoxeQkF z|DjAN8Cw|}I{hzIDhUu~!4^mSv~#bAUC(UqY_T7`_*?oCC#+qdHAWpwJ) zwL=K1e0iEa>HZohRUSv)n3||(nPy=TKd*3J;QS;;0-e zH>FxA>=E9f9fwAH)L1QS6O<+V;E6^I*W3BdwBwA}3+$??I*jOReU50!tUELCMO_f0 zh3j0*uT)qe=`68a0@V^@xqwY{f@Cjno?Q#AGP_f_t39ZQZ+Ve07Pg->#a16p(a!7X|8?vpt@id*(X!s^4J z4|B+I%Ab2BFBG7GP_|97T3dy!nNHG~4a9OR527UQ*c-`sZb@b18gwnvQ>DVAi9STD zr!OTH;|FSGSwRSgVZxN$sO!F|J3zGVE7!G$(`*ZHlC^+7(kB4aeP%}^ERmHy^c5^yBK$ppXW0M&P|=n;)sBZGf*Mrhl~|NLb;HQO=-}n<@9owOY@PQt-(_1_ z*LIzT!$j>Yg~asC$5DyuzXQ=rr|lWnM4q#MMspry>i1n=(Iw+viam?Xggj1TMl zYnJ1%BK?(r;hraSYq>G066fjCTp3r7f~bCJb^8FJ6Td{fQf*sieLWayl8}bh7=4c4 zFYh5xX1S)8yeQcuHG|7hZc+>ZJW zD}kgkI8qJ%IW|c{j3(b9clGxjun$J!NK&Py)0WpOoE z4*F1NqoS&ZVP*XHwr!MYT1Ui3;7&4?Ru9@sU1-xO#oe=HtCX~Y>ltjm;%*VM%&nfU zV;QRQpD+vLn)$M|OD@gH3;OT$MHoYIW2Ds@ZVFTq!m5xeyHv8xCQXqP4_qnDNo3Do z*jH0^rn&|Mx!h z&jhieGJr25N$2AT_pJ1%d5bJ9&hk;*89<9vYgsc*$q=CJ%-0rAP zz-?pkG^4+ci@)9~jR=b*t}qZ*G%tN#pVoC+9p`@cLk-Xb%Gra~jwQ*DBj@{Tf!h2M z%n$4ta3B+;+dVzeX4 zDT?<2L|F21_ReOpKZXq6`4PrF@tqP*#zY4_-C{>bOQaK2F#p`!=q4XChO-19$#fL} z=eTtGaOgoNke~x1gc^9k-TPDQ-*2z@f_D#qx?oM~cS(IPW1DrT)G#QI%SusG99VrAtP`48069=JbktFd6T1+h<2TvV(F!x&fGci;g#_3Ku< zu5q7#g4+?mA)M-5q5xpEBQ7gp`J6Hr?Ex8YyeuqRx>+w$wr(|nrn{WlH2+X#b%|jg zK$NwBBryhwX6Eor_Z7i+ap}55JiHCqx6P$h_pb-*XPIZcO{?#{YYNPsVuJb1&70;6 zj>%reQiURQIa!}I==FZZrL{AE?0dD&PjQm5BD5IL6^3_K6 z%Alnn4v99Tm0^?H4X*n&B7dx=cotpr_4yiY!_kZz@g6w5Knv{|EvX(cUxiIe-uyPv z>n-+H!OIPo;B)+?4D#WBmZra_rTD;pq>g}!(gT>7ivItoDALCICjVJa{^Hl}{Xq1F zNB)v8_x20r&`!>}$oICBA7La0)n_T-`R;<{QM`G1YJ*cT`}wBI-aH}gQY8B8vMrO( zxooU?c@kV(xI^>>Q5>;c2&%D^h`LpD2ON`B5>lF04OtnSKwb22^z}FZeeF)fC~iI) z7@Ndwd=&;I4`y)`4`=qS=@3Pw4u*!pW9$SP0QXmq2UmupB+q&nm*6LO?vLdaXF+kg zRKE}PN5&6o`eaETf__stQbobCYgN-MYJKJ!V5N^!;|cl^HaE`^GNc7)@%{p4CYfL( zMUGuWo8t;rO8x+#38(;Q0_RiE+8!g1@&@q{m9hFYtWv#&E|&-{b^!SLhT~^1tOPY& z45b7lP=o`W+upn>kD&PvV345tbUO3;D(|C#xUgs=z5LAl=ugPlgfBavptY8!hk&iq z0TL64#9;LLDrgyAyHe|X-2lSbM=F*VH70ttzskv$B8~U3PA@k-Xa$mFfCkk+z}NGv zK3^T-M48<^C3dZ_$G795`IoA37^U<0{G^Y{{9Sstb27ryGtg+`;QGvn#^qJNqGlY4#pS-OpXJa-Q-ZH(-s_X06+f)0o7Pqwh% z9}f?{3BN%D;9e_1&RjlwbY^VjzQOCE1s&g!m-Mxg!rhU$vuK}NUJk;2fr_cO#%38?O!p5@RnpP>*?}Dsjp+|jTU#(<%%-NnFNqB1d`}f<;04B0f0>NbwMrtT!)<6Ko+9`hTYm3CQlMn5-gvum|47pRqRut6@%v36oeYV@Aj-(o2#&c0|a5^JjmdQ84hl0AW(*`~oADz|;GLqC%?#XgIRk~kX4QZw^s4!w`JsH~6c;*<04k5O? z+YbRH!(PxX$X4W+lR87H>~sHOtKgWWX4}&#(Vys+gGVqRSzQnch=F$F$+Q7s2Sri^ ze&jH;G4-Xy7S=RbRFZ<@X&OkPo;As2g(7-OSo31F=H_5b0wOMyN%yhik&{bHpkj+Ju}ajV9uOiK%X)+9nlA)8TFs4&Q9oUSoJd{2|ef`u(q zSt#BH8N2BS?=F;FZxZoo2~&T&@K>H88dA-xfk-~nKeODSY+GaeU>{sr$^=50dIZ)w z<+6d%Wt!5L%v*3$y{o$SGi$*iC#beT4i$g4!-yHarEI+>TB9Y+R=jAqH$D!^v#gLs z(qBsLa&4Z9N&egv-ph!UB}2~Gf-MZYe9M9LWJ|xtq`e(kT}^Jv*AqAw zwUJcQPHKCQoj0$YUxvjNXy1vpi?3D1q>$a~PfOLrSHzK-rUf|b#os;sZGee^M(i zpH(w-ybqCMc+dfZU{2H%R@D)R&)&*@QGu05r%U-}A5!92k7>uWSD<4z!@d8G4%y=T zJ$uKVO48d~CqZxOI|E_LZdZh+a=m16i_c2ZS@Gbf+G-5SgbyhT^yQLad!rhq`Hcmj znIc7|I%p~P?SpvU+joIY*u7lc?w{4;=yN{1EQ}4IGj?*p?=DnVOj4OlRdO5^84eKN zKvbgE?|<3>TMN1y+da+e=fA+yj!#h zjq0Y0hu-tK9M{;}D0g04m(Y;)dKuFaT)F5#a4EhIyx5QsdEDU0_7Pd<58Gbj9@zn{ zlb|))n2!y!x{HGpVe3E*C##D7nfDA_tV85_y# z8(JD0{Yx$TJ8qz^rMCV9*(ccX1)-6Jftjf<*qEI=P#Txqh(`)?_i$EVG7+m8q&&$%xGR2q4ijfEDW3p&gnIreI7YSn{m`hX^Plwl-R(r0Zxj7+Y zauxZ!lOzdPeNIS=1wAlZ!;H+9H$^Hg|5Sl(ADK%~eh^YKXIim$|gt zOgzQ-v3cQhk-mvyle#@&%!;fPT?_X63rZh1UuWSI>?(%`-D5iuvE_Sv6J-5v*03pF z?VZvcCH^4WA)4I|J;r`$SRM5z%6@(_YdeAmhK76GRP((Uf%&b!*p!`2hSTfwx|ps26YqYX!4L=4E;tg|iS zS2F%LU0ByU#dzSN(7h8SPnMxr&TEy zC&K@r>>ayA3zIF~v~AnAvD3C~?lgAVwr$(Cjh(h_o1ImstM0g`dyG^42iCV0Z^WDt z^TG1DcuG$w*e^(V*doevDEl^mlp2Q>kg&*7czD8rrx$a&`zmbXQEQW21Rk25l_+uk zxI5U<%kue?K7Z(dNskdq7Sgh8}a|}#%>=Pl{=`Mk%JTF){(M-(?k}JCL{q1mC%u29YbpYb1^`WNOqR3SNCs&r|Bo->WY>`;g3)o!K-zwMJ;OK!QD+ltLO zS;rDhEr&jVmwq8BYC1LVjhnYxfF+*L(ZI7JP5KNQLlFh3spvzQ^;DKD&Gyk7k9hiS zW!{VX5noHsmnM)E9;kIMGyoi^+`4b#0)sRTxhYO(0eG0`hTL>=vD5^70XvCBLHf@6 zQK2KxCwg7&1f=PNz9;zF8)nj<#6ffGNIqdHDqo0}-rIYfJB!#uT-6b{gr>RfzZY9UfDF zcIRC}=1FikUMx-kZR>BAZ6ZC|2-x2+UVs*w>m3qy9TY;bfNCFKWHG9Yq2sc<(;F+3 zIkL4rNRjQQg1>B_WuaITukz`pM8LwVEMHnPNvEJ^bhR&&R97!H$(quRjD)i8Vh8%b z8ifLNr1%P<#_9a$v@AyCO4UTDu+rNl#asj06|c(rm_;~cfl4RH>EL3p5H22W*+x%2 zL3KQTVNbeY)4JTJqJzyVF&x=kg{C!4NRMRpLkz-<8V4CtaxA>+R{{PT(5?MXo^=_8 z6{!rON~zCIq2Vmyu_1#nkq@&RT~oSUxQM@=FSMb=D3|ToVC5v62}x}ZGh|aVUYy$6 zh+{$#@P-)RUtS+;$;S#FzuPwDlF}9Zyq=m;DkK9q(%Q0`vTEAHrana#nO~vdoP&xN zf&qTmZf+7y@Gi$w6@D>k6j0ta^T^~4@u@x2myyiqQUVL*1@jdB$#N~z(-FCWj$3ma z4P(nQWhoo2)W-;N4$9EB7LHa8&{aK9By zgggV``;*k+S{JRJg{bm!A8w-3y>pCi=A`U#YXNtpb7OrE4{h+AIzFML9+Qd+Rr>zR zg8Bt9C#Q!n&0F(0C@@ZBNdyf6JfbMXiJIiOkokgQ@e@t^!}+NZXUkAcvUtwf($@#J z=CfsV#k-7ZLGe+b@LmyxG1+d!A=qH5PfLS_L}LqqhsD{;^;(%S#OLwf=$!gj=E%PooAUf0n72M){R_k7QAf+If{~P>Y zW6l4mqmukjZNYEj|BHb7BpJQx@so3=i2wjV{~vxq%J|=7(9r)541kaQ2bjm9{KRnC zT9Z%&h5b(^S2JN%#>rUjA2FnJ)Hc#K#Xy4ZEzB#GJE}jLD5x+}QqzZ%Y0zz(W^zAi z&Jbu2*ZzzkT(foAw)&ZpAq2D#5Y9M8CxxoHW;e2x=9Fkl`JgQb)8`W=Nc%`h1*Cx* zmWj$1vyq717}-W;V1iaQX#G5J5x>agEcX7UO{&2vld5`?NI9BxcyUg}$>dE4b-JKm3B-i#ss7 z*dKmDaCm6wE?3qt)594$ld#DTzW}mk4b+}_({)M-jCs1Rx6D)&^9w+J&ilwGXxKp$ zj{CFU$ff~y?UxisbiX{D`lD~IEdkk_Oy26Mlqjkrh@#s(v}Dm=4)D%odl;p_g*}?6 z^(`=ofsp&1%S9Q0feA=YjA+q;34JC4i+5>o6Jn@ z3xk${vZE`iB+?Xz$-cvgG=or-kA#Z$qTP`3Q|zX!OO{u}Vb*-l@c-0?$)kVRYqgaLcUuDJ=;E8{AZsacFcIm zyIE*{QX)7sj_Y~Fe6XAJMHoI@$b(Y;|Y8xjJ=uM?Rdk-g9nt zKFuZq_DWuu?;HT6GmPb9&VWs#!6%1O?HG~e(DHFhg(yn1`-GfEB}CND>+@X5787?z z9yZ$DXx8^8G^B|usaoltn4neaa!yX&6A3%?;hg6N+heXzEwpupGi|r!g9i6v$Rr5O4-KQ99cr69S-m#1nZSQBB)LcYd&=g=b{KDE}cEa39yoP8>E?pyB zI}Mqpdoqo5qetl898#G zxr6V+t}`a?e!Eo#yto6Me0j#sfQ8h`W@C}k@$Q?DqE*tg7-K)ywqDm@G^@Y9o^ZP- zAf?igEz2KmDWopmWf~WF@!;&C3VgsB53Mom=uHRn!g~e|o?xodu}zma+V6LfYsYrF zDr+UYh+_XGWwQ)@u$*~uCybHUBV#h6u*ms) z>iB;LI{k3gx}ZmWTz}Dr?T+f5vvqU}Gwv;HE-=!xi9|OFuWrZ}CKn#EhJQTcD*geo z^3cEsap1T=>Uy=SL3WP4dfNEgX*4@nds))kN zsD9N+JrFSmE;7<9eehuOJ3Asi8jmx&)3>ZTM%2m$E@hq?=KT|}J4_TsylDd*NTDKu zM52yy5~r(u&JM0Z%0)j|&ThfuKpJsbP~B|M^g3dvS9x|mc#m2q^zKB4Bt9c|*Bx^zmIbrvJTqXZY=ZIw)BYaCR`k!ux#`Q2lKlg&t6h^Ucj^UGn3*GW5%I%W>q%-Q z8I@Ebg}zFSZBF{(tThSCqpj2hD`hy!?yCh7*2H{QgeX>6&qxtogJ%6olwWhUXYlqj z_w_lW%jTv9@*RH<(}Mi;XGwRmFc`C0d^^=-_J182W4vBDg}v8=LiDxxrThu(ZB-2PTG zp*KD3pM06~QgowDhCVB@aPCA$Mabwm`hIxQt_Rme1O!_{N$mH;ba!1EaCan5MnzN13v%<@OVSy&+E;uXTV&m zmQMt)T8WLVEk2))mYu6*J#Rq00psc2kug^3@^-57by#is`(PnDKLJftP`Qjws=V~R zAGBo*6d7sC%%n!a({V)m4zw=YOXz@=hG}GX9Z-2Xv}gt?#Me3#I!{2cKPvTI%Y$u^1n2Sjh-EBcUot(YS63>*`OPnitqfQ-1*;&-6?J+Ej zY_MHzOA9>zaRpDndAT|jBhIOD^cm`_GD97qjG4b<>|&(`bKgj9S#oSWQqi=-YN%&B zNdQalxK#^M_(1;n-D)sie!n)wCN>>fJdB9mR(4Mab96D%O>&BPC=gxBJdxbuQv=Uo zkub7&>1kPJfpsx<(>*MtqjjJkJE}(FAU^P4d!3w~J;8V_xOgv*{9R6e$zEU9!k}OH zGfRZw-F`q(bQjac6^xzJm8j&dQg~58VMccU(Gm?Yws3u@`L>RU zIcVpNLH>ThA>EUxfZmAF5 zBU#rnbk0NFvsib#7(Xm0FrT&Y-O|60T#fb32VC!12LY;-fArYMCy?nUvL%gTcJlPm zZ_DhguAAB11}4!M&GrQZn0XA8Z1b+ao^Q2yT%Edi$*PjXHzJW*H02&{()YaFMWh$G zTqGAzMX;@gh>~%V9eX*X#2FjqkDOEp1LGr%BNbA|8%6d{VFrJ+n%xfW>TRD{2)(F= zb+Zo4R$0Zt8`%1lC=*RcO-|pM&5bzlHy=B!E#Fl_{qMaKTwtJuf<%Bfgh&xBK2(i*nj#v`hqrCUu_~=Mo2%Q??Ok8pvGkBuB>a?xO-St(Xy@n}Y%x_YXvn;U>Ip2aN zA8MR8z2-%@@pdl7!L}^yjQd`;bTfP%-$xX7K8D~b`KFp{RZQg(~}d62NC=V z43YYp29_0((N0bZNXrbgSU8lz;VEET>c1XsfVCVTL1Hc}By6&YXz55C$^O3bUOJ8V zLE^z;h%wCQg$V0fYD$Ucmib+m?&tZKr*<7qK`Oum-|${^WJwAJwY56Ki}(pmLGpXw z%Me7UvKwfy-F#He1?-B8_@|r*__&7NNi{&==5eJ7Bx*H{syWRwjYpIdRgIe{L=KoN zRwqvkB}PPGDvXQ?Xu=RF`AVDWibg4x=*%{8&w<-R)wdJt-HYC+mUhiQ~#; z64=WiGfDdlKG7m)cI-Z~l==uhE3oQbd%dd}nm2%Wb+FESR38+BF~7jqT{$=$E=NpO zI`o^^yCSN??1$7uG*T?}?r6od7%!757(uaWqE^C_qT1nFGLVKU@iRB-F4vGI4!Eeo z%{xiNd88?7s;JMAI%gUTD{j{bkvWNP$#F##7WcCtRoza1tvd_8o%v5IqH9fli-1R4 z<1*!*wV0x^Uq&D8YB(+0Gmz=gpPB9lKyku3ah-Z%tr^2cy!5Hy=sv=-22!uhZ6|; zJiPi+QdsjySB+(JI|p>!{o;{5HD{;;4l3(s4J-`kL}7`Hw%P7dm3WEhxnc?jo)e&x zd_c$dlnCn_aT-~$=)8vrTZap^j`z`cHCiq*3%E|7EEU0%#@Q9 zm#5WSI4szMqp%#Oi?FAaOJ%SC18%-wR--T0(H485{$ImxeR0+nEbeo!kGxVDD^-AX zf6v(|ikxIjoL=Wy8J8=R+6O2(mEY?1w zUHU+0mCuGM=pUHV*?u%Ud*VcK$0Qbgo=U*tS4Tl-uTAMd<(@k8>ZZskc5^$!*Pwb@j$6D0^y z&+YG-oOI{OLJgPMk}HwqEc-xae9>F3F6PXc!yZ&qgAs1T$gQcn+=i?}|6n7jOEi-V z|0I&>%2mee_PX6y)XH?B5gD1WjPfnUYO?kWx~x;HIzqt{tU%Kfw!+jqr?K406PAOc zp?+U?e0Q%*Hc&P#oHzldI?P~+#}~qNK*nDSz9Mqo_|Bp#vR?~ZRPMGYD7%|#!VlAc zF8X-lmWXVKLde;dDa1QJmy%Ao6oCdUMQYFYd~#Sc{2&O(Yb#>LPKGF<&JJ?G<12|h zU?uz~zb~FXGp@ce1k7w84W1s#@<5w2iT(CFt+JehHEv2i zP#6Ge!&KvH-i{G#X_Tg5%dEDVRH$kng3U+E-}-Of*=|{ z?pkP2k$yJ_FDMjTTI&8DtXv~rjHl%6Xysa4gMVuQRS71G%&=X*3IPrst?9aFPkC;h zn6@@LSi5?E0W_4Naws%1LB>EQg{lmnIDuO(G(_o|m|!r8wa)O}3<23?+)RB)v_^l- z_Pno4u;qI-mU~55ru&S}5rklmLkaOJ4lh%IIVBs#cXKt4yiycHMut$@F7~!<9LFTO zh3n^(@o*MkF)}@bqnFu1#dq!kV1xp?3=6MaLcQy|cX?{KKNORaAqeR5IfE9gEjy0w z75~Ze2P~IUARd+4*41?o=y}ciV)|GOw*H#r;s40;qjC%9UL5I%kwNKyY;jj*x?lK2 z6!Rj}g{XDfE}tkRi{WHor_9xZm>YQ-dtxn0=DYaze^1NCeIS_J&)u_@+te|)I9t+8 zzB=9E-yZz-;a*GWmOq&LG#9GaWZtani4dBjWpf-(GiXImSGH@Txrkl=UfdAA3|H8bY;5eh${EWRn$KAoALznyzAvvXtEgC}I)%UXTa)I%hU^LGT{s)ZZ z0l8wDVSj!EQy;{>rpyD28<>ZV9`6hIUxLX0DUFl;PlAZOv%&vO9GBm70So_e-S2-~ z_x~}-=-=NEwskhNG7+?Ja{T9rSEcUpbLEBf?c?VeQUkJ^y(Gd0 zX;{)Vv}>13I&P|lq!NEuQ1f>SGrqoP@Nz2;*8dmD@oNTn2&e0xkJmot7o1{fxNTW- zhNPm&15e-tNenna-~e?~%$TTH&{33b6RC~y)Ftfz4F;oE5nWTBS@aWKGSl!fShsYlX@QA_ z%G5ox+Bnp~%3l@8c~M9W;Q)-i;Rote)PyNv;_QuKaE>Nk?AnLvzWGBP8Q`r)5J~Q z3sT%b6a`W+?*t8gGU-E#Z3%>CRHiUL2r;;GkP(7VyL#o48KFT*6UJ1CF#1uf?8Iwh8{; z7<%$%dlS%C56@@J8Uv}dJTnA#?0!0VzS{A2hIE9ycdU<50HoUK4qWPtoE#c-0D8Ajx8eB>xh88fzI6VJnKFe6Z+%;EG&D*VLK z^M|!0HAK83Mn-UO_|MbpkC)Bt&>1lfub`fejqO0t75)r#-apBA{R|f&Ps4C;E*$OEfGSeWZtYJ?& z`Q2t3T-jQbJ=NmQnDV3I%m>#*8QwONYK%5bx;4Q5aYm&tJz@N=)d=-JP=vd2ri31> zHPf)3+o_4dh}f4@=ooyX?xe)%1udZ%u44P0)=?4ki?m%*X>v|^3q(wgnwv;s}GPq4!Hf*TB#`V5m$7_?ME^ddop24Rd7((6>HNj97uf_u?tEIE}+6 z613RLbfPIzm7tLnB?G7S+9lLgMSDO{UjD3nL5wk-Cl#@kd^v^95yg|XSF9K$MoYluZ$q^yngYVH_>*`sOl$yq<(?W( zMi*sH!rU0>xqa!Jpt~Mvj5gU;MtJfSW?C~@THgDfDR-C^G3`Y99ZzW9C#OC3JKj`I zwxEm6-ScZu@WQfL@(|QCbFKAo8zeqXccTPxd{y&6#hdb88|}J|EojN=x2UV~Jn8Jd zr>r0ce~17>u9GZL&&i_cPD@R)7+#=mH?$d`*uj`a+WsQd%r z>#XBn+6MAGqWzRvA{{RwF-SXy&?iFdk+sv!bMA72NM8nu6m^Ak7Y4dHtnAit)eM3CX0S^qb88s}B0Fm-b^B@g$kmx0=U#4mr0f?qr7yyIwNP4uo1l8xg~`DacpTh2ms%@HW!DnoN=p18 zeeO3@GV6>&={?(Ds;YcJ_jMkr8gd>k+>%mZN-EsM5();uGc)>15-1bT(mo`f{lCxd zqhS^Um!~`kU4(lUVX9c%y>0$Juk7&(Q~eHb&F8nJM2{&S6gzJtZ3yD=^$~>7>(}=o zxqt>XYpiGD!+`T>M>kSi;)JX7f@<_yS%oPtfj{%A9tEbiwPH9`K$@N#)q5>D79@cz zS()**HY0E!Fr{vuF$I(N zYVGVr!PqGPy~qUIyP1*u#lIX7OYO{D%!wTa`m|J=Ev~&?Ge7zKuHUf*r?lRPlzqFb zTK@`pEFlXU2M{77^KDuBEg%s33b|Drlfg-ea)501!$EktNDY%tol>f8R`c?rW|zM; z3nlf=t64}05EBw8$jC1n@l}IU;aSdUGM>T1%MvoXLHFS@qwOP=%Cv6^7qYd@fT^=L zj^>+Ll;#Hi5FL(a(^zSKx)Z6Hs-!JJp4}OAF<%k#!u6e|s}GTbv{!^AmRh~s*?yAt z?dX!b73(^31OI|0F|+RZp0{wJy3`rD=-b`~{8xkUUyjMNwcRhuAGbvO2Rq3AA2$g9 z+MrXiaQ`m_H%ZmNcAX936OZ6~*Vn5KT&yn3?eYjTHG#A&Vvy0wOot5ylvDvtO+r~> zJX-6b=NgxkQzFA#9f5$z?b-GDYLhVpd}JFRZ|1pClC(>Q)}lx%>+~@?ImsbDkyIdV znhBG$2zm~(3l-bSr2ogjD(ZLhNlHnZG})~sr7R0qzmYU9l(GQf)?&IdDvFF8vseq2 z9s*K>(5#4};ThLFBi8E2^5#G>E;2F_z=!Ihy*VvVLY%gyBu?5%s004RcI*YfEHc;dv#SzQLr>*_ysN+XSK#Tx|?U#FVaPP)u~~T}7m!F0Ws9 zpp)UBVRy_+&zWk145MRdnmB-;pv+Noy)ACkMl+y4v$dlCB#wOsF`|L7;H%RC;U$JC z&R%79Bg2H}7%hQ{$0hP}GsL-DHO>S@s-jA!vUHD8TkJBwn6ci0Dr5TmU{kLyZCdfp zSgBKPPo-cHGvuz|EBZ$i#U|v2cW`QG?*rD8%xwPr^enrfHM80jeS%bHi_To|e$0@S zx2GeU#@xFlb(@YJD8DF?c9XS^UZJ~$8_>ME&yprfKFy0{7mu_Bf+d0y6+3KB3~4XY z3puglTVqig{dW#$Cf?Z3`g-Y1wV2ibEts2+ifzoj`aPaZ1jWhh3Av_(4NJNikzmw0 z887GQ^NF<9NHRNe{+AsC1`e$3TTSRya)Ot8iuGPO0JvKKuEm4Jf%jI%?O$l#{pWM+ z9&G6+?tz5e-|ce<%DeAAr?FukKq#EniMb7qG`cnBPONMA*%5++d#giPKSz?hZoFJ? zb>BihJsq1i0k9#)>!JSQ$DW>^uJYk00qRmD@jez0F%5o#zxhD$sSzEG_Xh%mV7lJ= z+h!S3cn&ffr>TuHB(LZnpv(m;^yTyLJKGkG9RW9mzN4fCyPInPcH4qQzeu8fc;! znQXQOxQ>OeG&(f`A`;BX-DpG^&;{z?Rnh6|F|Tq$=_b9QNTn3}4s%mZPhVDC88lbh zdLN8ESvr%dPLg5;UoTR0f4z7pjHuoCQ?*1slBi)qw0S#YdadR@wu3>utXBqU z2G}1Y!Qo*0-43-cWi#)7az7B04VH!Y89~Fr&L!d#ewm@ zk2aI{Ccu}}@hzKnOneF_J6gLAm*(|5sn4weA+U>QNGm!Q*$p1O0|8eN1Hi6;_o)LRuP{eki;6YSsfYaz2$&2IO^G~>g48qAFTHH7 zBpS)LFjkY5_vjK-Zv9C^q;M75B9U9EAXyFrqnyA$=;Zzzh{|Bt1R_IMkbtHYyTkm- zHokGie*zX`?WKlcu9CK>Je^j<5S|+nLkxprP`C+P&p#Ka+l8s@C}KH zQZ3RtSS;t=A`d0%SSC@FPsi=x(sWm8nLQzbrU{Lq8lr_wqtuUn2qne&)ogTv`NVZp z=7`Wo!;3a;5==nhx^&goJMqNIV05j51SA_rQj~(n-jssXOo0ReiK-14bsA{X_nGl& zDr3EEWWto6gm!-Khp*T+O!+1V51(NwlW^w1w5DZH0XVV0v6bk5042j7VA~)@YK|a+ zZ8MCDrx+#vErOEj4x`N7<0d0Rzka`Pr1kW)Wel9S1LBL#pPsmPW8^nQv0gbCuMOpU zBVu4xF#oXo=3NM@r;GQWPnfx~v}KQ=%lAB)diXM?c0Np@h+A=GjuUP-56z)kT;|s^ zRy6<^_BRG}fZl)3AzIZiSY|^l2WgR+*8OUKjWbItiy}0@ik%5XPhqwZibs5pl`mLc zQi+w|whAFo%dh>>|7|{4+m&;3xp%>F-76d<050q8&sic6pfx6V(26s+zpo1}>ufr9 zX9+Tc{qX)-@MV9E+Pi6`oZ@l?jSj^qUcQny%n5s$u@?E&&BSce#dG`qRjP}L1Ft&} zG#spG^JRucT^IE$Z&8$i19yMooHJ89b0Nd1Sbgvb&IVT2{c?YqT#Wz8`(&|V{gQ1( zUNY@31%$KU`sP4DV$ieIR9);r-K-uPG)VN3&<&*-q6r%h4+l^0)%>vc1fU895X;fgPOUx10s6dCIc15N5Jw`eyiK-Ju!pDtc$IPDq z!dPDRIQToX)`YoF1))|OmhjK8;aPnivP9p5d*KQQkq^uo--2?sH)Zwtm6Cy5{oKd) zRI$IlVl9I@8%Mif>GSkfv27d+B+*+QiYlAdX=^z9OcMLp&d&o?Zp*-w-Ko9jfsUIU6` z$oW0)<@OX>X5vkp`c{h-((q(FV7Ep0y=HPh+pcad--Q95+(A8`33s^RLgcU}Mb3MP z^>D%~TLi>NG&?&jtCsdacJLSp!jq12dP&|czZ5zfO8Ipm1yxYuY?_o zqsxd5P8}w5IbrGK&=S2eJbnAO17OGz5bYbymtiQliB_^miR_`C%>i2EbzQysXO#Z$ zw8lf7odmldjjSK_KUjeHcOn1x0%@0;^-rK6!q+s+w?T$?X;yjzY>~u13G%=%K}m?A zV#@hbaC>s2q=th`CpLD8FAtYT2m0aSc?(TbUv3MRruU16imuD5tNlw_SD1aukd}WU zr;5&seHG|n!1MeBD(4bML@6LGfoxZSQWT3>m-UM$)x8)qN<4>)`bgmtw8!xt+He!{3@3pU@q?u za%zWoHClW#okRJa2$@|8xN3my;>q3XMTJHfh&btJDe4C~HFTqBpH)|xD^8AioVz$k zqcIFFUy5T$HhV5?o)C7P`6G`R y}F(`$lqBMpA-cJtOxU$WIpmcTebp$F!%bVP! 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

YfAcBKo^VgZ=o6#lNtSZ?(mzwOQ=z1 zy3P_FwTg@<S@>AX{M$X@&U(hLlo^P zn3c%2rg4E1`?cRtEVCzVw5crvNLbWns};;F(V1*@lj&(35cN!Asgrmi>Mi#-0uxJ0 zy0Qhyd4Or0U>YN0YBwbl#4xwWqO}AQz;rJr7f$TL8JOC9!CHpQJR8?f#S$6@DkWix z8m|GiIBeJcq|fhzHGCp`?)Ke;JI5#8AD^T}*Lo`@-7@t%Mdo{bsvfnqV!7l zmF8Y%XJ>0?S|ABSo#O%F#*C%?jWZN#OSbeOYf8Q?iE7I?bH_KkCmP61Da@g({~Fp8 z<(dq7hELdKW2ffL0psPIdmDf8TCB$q@PXN#$v>}Vry zub~)y=zeFCW6IK*KPZbgPR!v;K3&?1wOv{?hRaRX%gv29sBJ@|&`010C;xZsJQpt) z&llLLLOIwm;larU*8*(ihy2|XQKocPH z3NosO{yRn{?OSDpKV3bYIVl+zC1yuOkeiDSH)ohb`0M!b;Mnx`@`B0tOOjjzb%eX< zswntVc=*|fY$2gq{;`ZrF;G)QXN0RpOc+OI;c zI$cj$pomjRzS1e95_dIrOSpHDrE>hNAy?V5~@_(uz93@AJ} zAE5yYBwZ5jk$zC-Y>qQHtnq5&2_Z`Iv8ZV$kaSHJk~2`I2OUd zoU;$#+U}v$?jg`g#4+Zp2Jr7X*TDj5*3gog&&2d?z%SzooUM^$Pt7(bQY$sy}x;XPC{u$4EE44@5NnMl1Haw^`mK~$fidvB#k_8}~ zyZc;1u;L)!sK0@1niWM68wclRBSTCDMCM9b?>W+0I$1T%xK0Ge1T$c}9E=JLe+i#L z(v8A6YM@powJKYhYCTkslq+~=szJilal!IJ!uJ%HTKL2(-v!mcR2q;I_DdWo)hmV- zbGGozJjmoOt9uFPLIz?miJh^TUL8abxJy}+^W2o9crQtA1dDNwP55+H95f;^d6b%U zu9GCkwOUuU-3PC4`Lc!qlvqNqd+j>2$iVbZQD!J@@_FFc4kcMRy_(=}v9m-?{O|@4n10qp|Po8^(OfZa#9lbzvS1 z$XSDU)9gtpNLW!1-2#Am#TnOtn~|(XEEmBOiF&3>IsW3qh9Blrrmvt}z!EfDfs7@+ zCq#UIzy+k-t=C=K^4^njUG5bEWwysX_&xa|LHzyi4HCe1*vB`&&xX7=e&1Tifzf1+ zfXPnj^~aY%!^hNtgQ-`k*qp=5(W{na084xg6er8=jChv<@AeYx1N|D9K!v zx&tq4!(4a*Y#@Zf&7yvrbRIe$O zCVF%?30InjxRredC9CM`BUE%P$?uLH!zh-OHVUm{UtFQn!unw`zDH8L&qt!FYcZje zFabi;N4QvX5i3WjpWe#__TD(grmLAh{F92_D|qs|gzT-J>b)Umsa4MEdVJL2wH?Lb zFp&x-?fJkByW9<+!#mGyR}oMuy(#4mK}mG9=mi3v+W!|KX3;}u29dc{fnI0 zlmC=W;!X20nu=H`q$(9mp;gN5^LUcFe)hrwd>6c010!e7R#~cGRPcr#dvI&9*8Xb<^fcG+aY=X{jTW$xrF>@MEQ55-aihFp~V|= zOfOEV(=WPNMfGCVBr#yO1*My3RFPE^8_la4ZHT^D3@Pceu&i(JE?PD0yb&F@hLpj>nTio{W&l0H(WnYC zPj1ER;8c_*gD7`HDwJD|5ViO3bn;QC?{sp}uL2cxqU7FmT1zCnlgT%`c4#&vQ3ec- zXNQplYA_65VParR(nCQHwNEJz-ro{|Vd-jrU^}oFYEHPYSf*nFoh4%uUv>9@pBEoa zbu6K!$81k2%+h6lL1Rz~6&8yF5b>db+xHW&jP}XdUwJbyhJQ`f;?N#iLxq_%}BL#?jYp z)jyoOr!7rAtw{X-;KRwqnH_iV^$PFNV7?1S_vxn2%bl^k4`&O0PA7oqlxv%+A&Zg$ zkV|MmnV_17Lk_H)cpE{qYGJWA6f?sWwsV-C-Afnb(P69tf+bsXTCS3~3C0vCkx5RA znu1yStkq2?hxI_NBbK`va!6n}TrqEe$cSK^jefSESOsNY!cOYB4PxQCarGqhUj7H9 z|Fd4b-949wp7@Axx}Go6^*kdFn(3{r!0gh?>!9sQui-b>d3P90Box~E8v*v?uu1?g zNs7U3Ydk}pxPyTdhThsiN{^Py*_Gc#c6W!7;rB~v? z`wi7E9Z-#=E-Sc(to!lJ*Jd62G-Za{a2%N(3`zY+NcjvU?#ujuEAq?@OuXJnZ%<5P zkl(F>!bp*fP>zodVRS=>DzPzQVJ$J-p{j)mfjsM-dzPL;dklD;$MNq|*1=uv6H{@cNR9yv)SZCvOtvW2sL!5kqKok^~x7qFdZaDMrJTAl44uAg= zXh0@X1~#m?BFX^Q9Ca)*s&KZO%+qIeD~QPo%5(`QTN^~vVR2y%Qf9{(AuYj{(YCSL zo8JLHXY?4{rG(2UXP3x_=p9y%J4#p1<|JJmPl79jpU@^-1FtlC!6NQmYurue-d53f zgvFM7SgXlRx#nM(>$EK)+ER%6!bn|rK3Ah+Dyvd*?2f3X2h?b9&z27hQ^9MlGidkQ z6Q~!1i@96F+B9?wM!;6#qM{efEpF9w*WeiGj@dGRcjdSHCDNcQ zUFE&~{wF?sXZcgJ4IMdc#IfE6aRa24>Py}Co;CZlTgs@jERE}zC=ySsz5Oi$t;8+8 zQjqYLmapw9l;(115?Xw=j?>J7YRfe_73p=`${5=L@QNdz=AOubygHp*N)C}+x84L)ui;x3-yYd75n%x zZVeo~pkfPd`ZYLt@AWLVT#oshf>RUUbjtE#uAZQeAFG7&s>({jdCXZh>I{{CVYwunq0D|^$Z67Um^)b;ayS1XnQ=(>yithY^#gEUxQ8-TJTMpa_LB|e+b^J_HF z{gko)Xd%_Jeq3n$%kzIbZ2oT=9p3-H_BmS^I+*?oiLS>==I;30ndl4izfuJL^U?p3 z5G;+A|M4baS4*3JvPo;y6zvbh5dGklzJ?@xnUXmLGoQ3nDniIRvJ-I!i_3WFpMW;k zx_Y)r!E9zy`}CK~ZupROVI@(S8$2zyKTN zlw$H!paHOakfk@ILR%~b=t4+rGmfdu%5iN8r=e3I{l0Yr+*kuu81O#y-v+>PLrTTW zNG5{)I`oQ2qqu(}XCzab0TKbCV}-!E$$C^;UUMcgr|e@IvlbKMhTod?xsUCU&psh0 zd%^0N%RWO#GL#56T{lQ9vm7*4iZaP`W>FTFNWIsI+Mof!N@suuuA6nR z1}0ta9vU~&O58x28QjKAUqQ)J8}GhmK=G_3sSyCFN$*pXK~;0cnEe)vxm&OV zb`Zvxd@fDjp5tT&R9)x=;+V3-CGDd!j!zet9A zpU*s}bSt*!yTr-VdH!)M%BzifW}m?F0E9+q%FU0~eX$WkoLT=+sC%{`MQWo-3(p^O zv)s1r^D7NU+d%n?7j9h?+fB0!KnM%{;jS%EYSd9YRSw3o9E@&#o=9JKTM&KP8?OaC zn<#bqN7K~Pmn?md?YXO3tBG(b6~FJy=oaq}`B~#%ah-_$ z*&kdD=Euh=B_*X^`Rv|jMX*nWesJ0>iuCXUR#%>I8YGx5p^7(^@Gb0-D!d31o)XO9 zyNd9K=Z}bF3y(KQU&*X~#UgZ0CC=-)UFh#LHsy3(>poU z@%8TfMcCecNz+PQq0n=$bENWj{CvW1`0~Q2NB1@;rw#?rXZU${+A0&kW&4Y<){U=< z@%GQ6h2@_s|rf58-sP?teXpbsPiyQhPN9frKasZ&H|^u9M*Z9j5^P& zn|$8lpJEEBW35j|2+r$Z9!H~9^KRiKvQpL@B1ycTDZIlGJw#;wTAQa}3;Eb)d(e}z za8Bwpz$&h(yx(5~h;CUJ-$4K7JO1CGOaA`_x@Jy>#x907{|$WiV%@h`0W-@mATHp4 z5W)NleEnTh7~7$8DZ63*cpWrO2|XrNx<; zXomw%C}CK`Qc?uk1xiY+Z;>@OQY^-8#GM8h77bHm`geiE3$T)W#%asI7Ml~ z)c(W1C42+CA4NJjZoZFEmBcL68=SKJ=}W6NeI#@Qzt-;hG>nuhv(^LVm^}6#lG-T1 zUSvucv<%+#Do2wD2QhS1mxn}AG=w2Fx}eBcOy|dyOD}D zpmr(dKBDR!!t_-8sKb*=o$2lkumSZFGZL$4wAHd+{z0`Pz8acl>$lD(wO=n=w!M(j z$x+x~0uY6B07T)fvd0q|?)R4gz(J|3y^`2-5F?;d_so^X>J2PBniAB+y<3P)C=jl7y>%4G&$lKlDFxZo|fzbAojvl|Pl-Gk_4H$EYs&~3AP1?BJW};Z=lcX>#A{&&YAGM=t*-1C1!U^PlQ|XU zU_0>*dLV9>1yh~^*GTX3ZmzGSl96XWYo>!`qe8ArI~batX7K6QnK6G2rUw3&xF{6W ze~vamazM*;@;qUWM%*0wTVZ}O$e5vB>{qj389|N?vT&)||o z)j7X!UT*pbk1r7?B-4uYo^kAFopp6aJz1o}SXS*+y=v2zp(0}M=U`#a&$L1>k?;nU zu=lie>#a-hsunoN7dVWqqT0XnjnnAQXJr#|7jy@sV z^xZI8MT<6J-!LiVT0hItGk@h__8PDlTfm3Lks~4?G3@3#*I0&EKAWf4Y@63MPyFi1 z-y%(Y*;xoLREVz$Zdg&RS6iTTu{oYy03MF%8^!NgyI*;Gwc%KYkBRO8(xEQ-Y+Rw8 zNKMnVB)41+n z3CLjLZOQ%oRjER5mHYD-^rR4$0a69oZX&u=P-ee&%YZsGYZz`ltl69v-9iXsd-F@s zAYC$Z5vuf$5*=0+KxRR@j0HikYQ0fp1U8QW6DBPKvhmDPprj&Zjlxi$gLEaFfb{RQ zmlG0HKg`ChO8yB&SrNA+ix1SmWPq(^SwHzhXM2iawQwjnWoq$;N;0Tes`sNWtUJFx2e^ z89T9MJ4PO$woTZfA{X3-Mzpa>to@L>o&srd%;N1-h9RQ^4OAB8F0K#n1D_awj=-eT^50)%N(d6kglIx{#L5i^~K8kDD4+E2ujAm|#IG}4eihWKC zW*z~6ul|<8L)r1dWHM98!ZRvX|B?j@KcT*U426R$f4di9E*KE;v1$}`1|oU8+$|h& zIt(ke^Q4Fo%o(SKUNA5PGu=v*aRzDJC1xju{JnjpVd25Xt!Oeu!A~UC1VCs(jb%W6 z;I%5IV&HtZ#1#>`ncsBwbt+C|&CR9G>$@4qp=it^Kpyl1M zIi8v^n8>bla=T&&5YqQ(<$~>QPX~KgLBR*{Lr(`T4`_23ZLy9>1o{y>>xeOcqb6Sb z?Tr(RxVdF zqr2)?GX)cD+MPjZBBFGd_hmN+N7_8b*N`n*z7fiWeUBd)g!|MywLp!U2QAP-Vco_+ zu!2JljHN&{^8$72XoiFZ?8WcboI@6l6-KLZ$LzucCaG7w_v>yq+9e}VTKnphxX__d z*lfNS;P|Ukc8Q)XfE}XP9LYIJoH)siRPQTw5y)Y)Z2DnS{NICKQx=_(t?1I5Iy8UBcrq4kJx%5P5g1$UVyn*}E-qphfXIRPz@2(xi@6iZ zA=10}-2HqX7E@B%Im;3npxKOm-&JKx%&W=m=0S?0FEE%}8k|c{@jtt#&h;q-&z24Z;W^0iZ=teR-}NZO@wXQo0Ggb}?zkafYvU}XcjX6b;_ne;ltWQc*wFq5Ocs@BD6QHRE` z$`kPYqZL$}4fn~T$pghNG#61cf3WiOFdzvSa&|kLJ3OqlJ5KQofh|?&x@Sg5@dkLnqlB%zH-x9Vrh!u1C``wgLJ0AVZK7v?OGr)?lV=-KO(8_B2L z96#!F7Rct~V~*7we?p$bpmPIs_j(Dj3!(l?f7mvYTrgRxS%6cWhRv{1u|-!n^_MpF z2P8)S$VAU^Np@MM*WG|7Unb7Mxh9^|sd(P-Edd_i`YP@`f zp#HRdvA(vvKEEtWcM5|;`E^z|$Jh7&kZ42p{{`j%{gnwI!QkHm&cDimLmHv)F`yjW z0_cYS!$sv^)5(AQp!Cm=6znbST%7+)CHPOXNh`j>m}Mo`c%$n*#3FRkq9{SwWi@=1 zp8?t05lJGBU@0x@B1N}&^rCPITs z8gc_jMIxry^3@7Si@yZwvD5zHFlkaYHK2VUI^xVgkHHlyox$=v!lDGeJk^df|B18D-!GpCKFQ5UN(v^V5c9D;f|Mxi*cV_&?W(yci$>gL*sf=VQ&X6rt1AHW z$@+YxLFBlLy5-9;4f=klHi%>`zy74FT{hd*;l*9%4ENBS%Lw@xfa+w=n%MWML#Bh1 zz@gH{&Kio-3VZE-763;}SK{1sY&W648_56C2YJ8c)%tU_mY4ChCR&YDy`!8A&_} zDf&>-KN#K%clt%mee1K^h|>JiNJeQDKl7jC3FWG2imIP`Ic2g(ID&r>vb(LwXAJq6 zg+*U*@Dl<)wQ(OW?c8Jixn<5@lTfhhuBa?37Xu^zXNs$1Nx_#T zhZ%YA%`@L`gKzpA>XeuVeE#M>yL4ids%8ThmzeZcQXW3u>Xx_+6KZV&j7!26ZAg2> z?T`7dcHNrk<9m;zAHQ&ZW|w!hcdy%E(6t+*BFw%TC->GwPN$&riGi2UT-&g>W|q^I zTsj}oWdW7s966RUk$G(VY}&eSt_x}AUtP-1>4Y!g6~vnuZA&uOIovoKeje%fnB2eo zW`h-}+9AR2ST0gB`Nf|5eD(CCmdteZqu&S!QnK73e`AP^_)<8`%am7X8&LqXVRZXyNCSU722ka?Meg*Go!9eRUOONtPYu zGxT$73GRAahO=WjiP+0oL>ZVlOfuS~+a4{wmqN952ojr@kRlWfJP!SCoAI0LPFkxi z8y}Yw_zhmq$ziGpBA=ZcRbT_+=7!Af>14j<;HJc_)_&V)ebc~&Y1WSFj7Qje59Mu1 zz?kbaJAq5$xd7yKJNMZjJ*!xA>5>v2o|<4?s{g(Qbq)+5jOQa1Mgc~=hJKReTKk^7 z&nZ~0uM}TT@ve=PRLEa}$y(KLc~1%WeHN*JT za1Yk2%kD6bm|T4$s-p&Ga>L7pB^DH=_tNe4N2|y&0^aVMlB;6OEEiLSz3uxp+x+Ht z|K8`r%`g1Ta4$t8D0O?R|6dCTJBmp81i+z~5giCf_Y&q3mJ>+yj*+6_T0s#|%@Z1P)W`gumcsl381< zb49WfTvCVjr>}3a_UNNA)-?AAH_m{B8B1alJDjs27Zf}kNd_4oi*PAX*$t)kp4vY{ zZ?dr$F{0^qVRLadc9^CYe$7RZ6S3A}Rahx}STmr-_e*FriVVRq8Ze&W(in<2pjhJX~kOU5y%$JM~v;>Ifo84|HbjQu(D>Hq;7fSKsH2n~ zY&>A?OaD+Oku(?}UiQhPlx+@ZyeSa$a(`wDx_W2!IRO-s=ORZ)(`lVO>d{|X(ka*= z@T4@uN=;&;bA6ZVLf80KF53_GZ$-?W{!4`2{=9etOPfF)4%_%KZ{6WpS3p;U5YO&| zt;Br^_d!M^O;HC)d(@da5+*E1VUZ%dC&qAfVvxyLM>gZkQ^=za(Y8tE=i5<+BnuhC z(KNWiyhNo5QVSueg&+f90uc;at3$2OpKLH$4!8DLjx?lg@){w?uHYViTXqAu74VW< zZoq5iWo2cIa(~vpK>%cR`syl>Y6;}$f%RB2H3O3`W6_gsA^sIz_dkMioqoQ6EOC@t z@=_TqS}%)Pcje587?q&@ECL3-xShSX0m4TOfUAohMz(Gca42{zMgav_Rr=juFe9`3Z7ouiM2NAx^^9h zqQAXXkez!Zd|wqS&THgj-Wq6K1{m9*as5?~p|ShqcNwLByD8?A7M~@muvY_hi6iBb zc(vL#nYD0{IN%NaxHhU+{zymiC|Jf;wh>bH7XrE3-65m4cUbvA*y-++YQiP4iVMHX z$9YRg!oAO=V8AKZk~h8$f4x+-L^CMeCv)^PS+7ICr_3gfOnGfV;ls7VF_$t*Bq?B( zt{WgB^|Hy7ZPRqjE}em_6U7{k=c249veiXD(lh<0hfmKCRVMHqZV++yK@2 zXSgrfBFoLOp@}A=YStki+Qrxuh+*cpcYgO6>G?f|_vZkDLn+%aZ;cq?aiRXRU16&# zGDqN^?|7NlNys#Pt?V^F{L+q3{K8eJy6#eYipCK3);saUlyapelRqRXOGh!)h7J#e zt`gfeWA9x~zKA>J>4L941V|k|e;4ztZUW4*r;T$Boy*7qxM$hvaJ%Uxn7cx8Q_5D| z?X<1e7$&fHzpd>8aBbj1rh1ZR*lphTf=*cgQNHpgj5M^{ns;Ck0v&CCB{E_~#F@RD z$oW}wTkw}x&VT))@{%UaQRHxs9BJ(X*3Eb$gyb712L{!Bf%+)Tb%U5|t8tUZThCOd zJjTCVSoLI61aoIN?W7=|ynd1Ei9vNNmiAyoul7j5OQ}+$7;9HPkC-)uhYh{8<$2(< zy=k07h*3!J&NBY12kIhlb0}V2)Zj;UHd~a;W|EIfQoMFjJZ};^qC};p=+;Km1L?>& zSRIW=->}>i4|*Dn6EuBnP046d1;6zEJPML>9^G4V@*fLnTQi640jROf((cMr<0HY* z*!d~<=VAXB3WX^nyp@9U(yUS>6H(~&&2o=eTM)I~DU49r6{7+jKENhWr`oY$iMZmR zDm)dl-1Cq7wd=wR^rF?(SKvw$l{ZgA`9NvVXc7@I*j(GSl>LJ zY=;*uh)?!1>Qb_vyPKt=YAMuhUdtiN9%ctb*|$NYG9c*_#l2Li*PO9b%sh0?^;a!B z!Zt}dPcNjow2p90NqnxCScHpH6i;yL)CnjPc~4HtbvKYUgB%U}r#lByCO44%&5d<@ zh0VJ;ePkYOM zzZS7~HMcQzRxounwl;N=v9vS&r$WNg46w@lryedxP1k-+0?k)^!oO|42TK@Ff@A$^ zzFNvMS*e5?q)}Dn(9j5xso!HG12{_U_1fEvZzPOt>FS7h<@rAQ9egxaD|m!eE^mYs zXHtg3siR;{2}@2GwMc>jIzA?WdK}u{;ukS+QH91W8gr=*!70F7IVdXeXe5&*v&<=HZ;luh`GE)E zA-6LphF}e7&&_bjOId#k)yYxTsAYYj&>}BLP)I}Or`>~T2&)Piw_!mVtnykQEPI{VhvN#f|zNGH+ zc9fCR_)Kqme)-B_rJG>e&A{3WMJq11L&A*(3?w+!sA3g}3&iJ(>B2We{8{(AQn2Uu z3FF)TIbspAt`D|SI>hE~+jeLB8G@w)E1nOwFI1#hHYRz?uutvSI*L4!6S3m>FI)XRbn}>wF*7}BzVeDnDS|> zBp5qDk@G2MY_}W%0UbR%=L0M+uPy6E`MV)4uT!KZ*{#!eQNXsFqyyU(za~1UTB6^3 zcV7$z7b`Z> zPnIJmcuffYk5Wduj3Z#S7{n&v#DQeDnx%Ti*wAYdDc(Tf>%g?gCd(Du;1EbAVeg1z z4xeaW9kC2vGN&nhcH$nM#EUBiA?7aJs=wi#~4fT!M`&gAr+fO?7^^>jibq?Nx z?7|{sW0LXUVQ=CU%xe(@Zpj0a(dOEH2$Q1iA|$W_e*d*Sgl`k8Fa7ZnO@(Sxd(LT}_kaBEtXZT|!ItOcgkIrX<#o{1tS&m*Ci%2qu%^ch4dS>`yYR}5Mt+NT zrDy)aHl_6n5^PvTV(WeSllalN(?BpPF>F|o*T%CO9K@7R5ZM0*=~E|bt1wTo8)Urw z2{0jV?5Z}bhBn@C)p`F%t%cCmXw7Awsz&OsX2@2DlM5`ggk$a7eYsSdN~>(nKQ>0> z)L7rN1H*r(F8|7C6u!c~a;ucACv#iaai=V;a?;RcVnv=owapFn`u`v9#J^UH5=J!R z7=ScYFn}dA|NjE-|0`nuH?%I%1n?fl&_8W}zYvu%SCqnJ#^n}dm&Zw>PbFNoIn)5VMVDc* zR66kFFvqlVI#fw&)tSYR&k@YFR7c|w!^4>LowR!rROiI>HsY1y31vBTKI2NHOw=kv zt&h$mt>S7Gn|{^|C#oGA8cdS#mi5a|q`S)IDE&Pa?PYW2*li<7;xqug#(s5moqlJI-% z&Kd%ijDXi1848z-k-A`nJgLF4Xnk}M`?Jv6LiLiL8@XhmDP~Y`KE+* zqxOOu@vApSsP9}|ow!>uf;qu0+0=aKV-=}@oNQt&J+KEQF7VNtrN_?m zTgt|-J1?JmGd81i!mE{pO@!-_Wz6U6F!zgpMA!_T$WE>E$ zplY9rasEV|w>B_HYsA)vD&|I}>RrU}YC{c*>Dk$lU_59$ z6Xp+EM{DoNt{Tex&5ea@hwNB$iHd50^Z2kIR%lJ70Ma!R8wWQpFE6JzlN(m!=b{4F za~3XI=NJBGC)7{`nF)P5=+E^tNblv-*Zb31{XBkd?~z(GDT-Enei}n>&1ea!5_0o8 z28Ualsa!q&$9M*h8D%}%=$TxE4*6Vu{@3-LI#7T6cAXJemH{x_J&|eN(NDRuopEyk zbSAGJ$-BDwp^?9HCa!)f+!s5o^M?XTJ7atT0^bZQI_7Cl^$UUm0vGkh98Q_VL}FrntkV5Qm9# zQkG}Mi4gP?QpYO(61l@eZOp>?rA{oMe-spLv>dI7cac=0xy4&?#K$QYj|8NEF>A^V z)vUp0t6>-&AVelba9GVRyM9>jVN9irc}|Lu)J|95fyF4_ut@6)$`|_+DR$W6M^a#?=^q3t zJ*;(G1~Ut&J{(4v{@&twX#c1CTJj(ffA@SF^&i^su6aR`-d_lPg_RX zSp8-Orc}91iuTrx9YYUAs07`r(nO_KuWy9`KuR&$M;uw688s^*xCd?0iT(n-?~jC> zgaTXj>d)#$$G$rAn>0=O86S^0p}@GiA-MBY35By!l&p)J*b?uU0n|&n&X&7 z6yYp*6mS!fh`be3W52^T9Ul>SLkAr@4MHgH z`Tv%FO7Tx(bztOW*=pmS44k^ddE^vq-H8mvjr#V`&8j(1E?A{dY+9?J!6z{x6mEdb z>QKYfn>*TqKGvLsmA^=$qG`nm2z0;CD(#(D#x-+NR9+*QBie#`HQ1`92i>O%tZWfA zq9FVAJ2h-bqa>zXwI_q#Vezh>%j(Xxxc%7${Sl+AxCO0jYU)7bBofaMADRZM1!?_k z5+_wub^o;QdsU0fPjzUu&V}@qL({cotzWOC9%on+z(7aw%dMq(lSKG2MCFnucWv!= zv7M?SZNE2Z4}PEHBh^=Xx>nlW{Q;nTSp`sXYaMShUX zlm3a?S^d2_9m907W;wJAH?f*+jQ@-JP>f|-1EydznNVaPF;Jfm6~5*MHP~901zu3VLM7-zSi(D3)|2ba zcCq;f{E8x*cE5Dkaoln9V^I$@MGEkjDRfQ3PcN}9%}>Z6__pz%?Q|eAM*{weJe#wV;>WTF)d?~|Ao4;QlGUt!Zl4_iO(6JHTOsW11+t=#28M9l!j`y1?J7he zd+BNvBaE>9k!cQQD9|olUWC+w=v%9e3_H*BZxh){YkuSU+ol5FuV5|LInF_-bl!GH zGdZ(735mETbFknvqSs{r8Uqpk4vz{`L~eHzo_uqXlsV_P>yT{jEAA0)EZIc4NV&Hb zuoZ`VX#LQjTZAen{(^36vOd@4f>>mCF(Z-4TH0yeX~DQjFzyA+VJdkGJ&WHuS1IS6 z)5NpdDkpW&`iiqYGNsUsm#X9$@uGq;>xH;sm1$pG#ldtM8*B< z_mTA%kJ3TYLd#M=BNho%J6(LtUD**tr~mk}|D75M2%U*v{%y5aDz+(?{wF2&wttO! zCbsr6$tt5oR~1qPt@HQy1L`|!v{sU~8XHR|1_C0$v;T_hoi{e1rlF^bW$p0A-_1u` zWwB>v{e64A#5&3S_ZgzAgtLOnBn?^17MBbgolszBx=tvIm;g|e8DDl3froW(tVxV= zEb{7s$Wh7c$Y;i|%RbKu+a5vXI2bQwcbwz(<=HcbkkX%? z;8jG?r=_tS6Kdrt`nvtGqEzlejOypF$HT?mVbG8Yyczn`FPja`c63-YDMWw=kAIox zkP#5$QymH)>xA8^0BhFC7VNA+cy4;Y%3ewzQ#;MYEg9pMtDJ}$b!UqT(%573Qe-3J zw5eRfs@x=m+GTaX@5*hDiUv)KOB95b-)c@)Z}u0LuAHrH#+YXjGr88En}yJ(wzTVh zcy3?+=NkU6hI41UBt{IdB2WaZ2-yD@4d*|X0;cA+rgkopb|$7C|FIU(w#N}i^Ao@G z=aN!|X3`7RZMC#4K^k?7@PXYCQA-IobPs4q$7W%LElK?L?q%(ST`9HH(p%woX4r1` z1(Y7C90*j&YY1PQUm23q9+ICjo{|%Z`76a0G2JYgQxRK{+#(eYay)mayM{HTO)7FF zPe>9traZ_W6iO_tmf%@Djd#+e)iPtanb9ibAi!X;OFC9BSkg_$X0xH_abVo1nyRzi z9I&yY0W!l}?yT5V$ONm-D!X8D(>9h%9(xsDIz_=|O`?R0I^>I-mKZ=PcYKLTm0@Zt zN=dAwy+~>;mX<70p%8yXxb2si_DMWYcvd+vo|Kcz=aNJvl-3gJ(%sJ5*gl-x zJbxae3^%w?uqVl%^5B-zgx$7BmAFZ)&Bd771zEwR_g|$1KEM*WSha=0b;>I3W*h%0 zaoq?BE>{(!<>MIBsD%0RLT@z_#vY)j6h*Q=&$6Z!`e$-PIisnj5yMdnNu3m5&}4-W zIqE6Ec0Biue(fBHN!Eho2*oyze1sADr-1fX89}L7{LdaHP0VIlE0MJq2zok3B#0MH z_g&M?Z3tIaJob|lbsPyj(hfP%n7AWY504d_S?hg1Ydx2UOvoBF>pIt!kI<9SZD`A! zv~il)t};L*rU9t&x3BMR8NU+{#AwB9p1Ueu>n!@vr8KN#^C(e$oEwylE~L4^Bxm3b?6Y=A2CK z-hAZ+9mbO$6zsp#^P%+wS?^*~sQ~wd94Vp`*>ByEdga$B=S)p@^E-M+OrUQfdQ4iw z-x%`+*_C_dJ(cVNa9;!j)HCpFeFpr^ zQ<#tpKMZlV&MF5KXe`(ptRKAVBb6#f{XqJ!u4wwwkgDONIbMqeTBZ3RU^TH1cX7G- z9b0OF4@HM80yun9X*~;%S-_JUuPws(5x#4*JjQ};BT+Kxzxfpko_j%Zg6~eHDK766 zvx!ax5!Bu&DvpOkss~cvh>zY%ROJwC5vCyz(b|HKhW)S3+Xp7BSL2*kvFLxWNg*D3 zN>JE{8K`nGpm~IJ&@qa6`umr}+1L61AsFPA$pteE*6HqbQHZ=zjIQTcE9-bGl`@<(s4j+{83aS*tixCZRo64k!_npN32a zvzcz%2bL(K$<3IjOOx7Z#H~sumGYARaQIQ{pwrV{2ivjA7!aB`--bQ)g;CF^>-d7@ zM0xuth5NW7En0m=NRL~P)hse6quwq{>|pFIsfZ1eVdAv_Na9&A_&R)P=V$27tx0~t zM-zb?9VCZ-H=}|yB@SOugK3WlO@pEpdhpi{RS4xAm{;7owjkx;pH8e|D)i~dL%~_O z!Q#&$5FG0FlZ%e{JSXb(TDq;d#I1GutgU8|X2*tBfa94NLE;C!_*1Dx_hMU_tG< z7owKW`Z9#zL7wq@BWRsDe9lN;qUU&G7d*ULg}MkeIUm*zD^!c=<5~$TH)z4ZK&coG zm1%4^kalRSy{85I%?K?MOXF%RwZCazJTxyiLt@qrHa7xJ5SklKqE7}h75Q~W$og-X zVXt6-;IL3JlBK2>h)@kGxnu+Tlu9uJYj*Hb1~1DXrNSu+N@;>MO*N~Lwh}TPQ|eQ5 z|4X-=sM;KQ)WOKP>&69rY8XOvIQ)h)U0fizI+fl)<%U$E!A-YBTO-w)u#mf3_|l4+ zfSsI3VHngD`%;31d;n~-@)am&S|$@5WSI@T5oU8BhV#(2!8CP!Y%y(uhKX|&oWDj8 zX9+dbp`rT<$F??#7n;&Q+D}VQwFw52d`2J55kf*vW*NfVok^GOT!A5Z)CX7IShYC* zM+r7;kU+&$!wFRhJYa$C8goKsBd^Xb{So;gzn&Pvt3s%wcalIiDbW>9i*o&Q_oB z{umWNb@ub>3NDe$7A$xjo50Qpi*Y2@as*9!m0Q!A>#9|pbN^ibP4GFZbN?2LAR>g{ z5el^%k*Ax8O5<+IDX#c=yYu7F#^Mvtbm@Sm4dzB0_u0PoCGKJ{&i%rbM;+M3Ay@A! zL73n89xdKOu0wCP4Rt&YqpyLl47o%PCcI|xaZq@@FTXF&5E~~1ywKUaa(R~2-sUqG z+6S)ZO>P}$>u(aG9%|4N&aH0{|9t}bSBGXcyyx+cBTg-VZTP?G(Ebw; zxHwtbng2(JW($x&A$>6b65;}U6LtTY5ce3h#Gb?^nhDX;NJ8}%m}rqzBV$Eo`Um!I z+g)5T?SN~w14y!Xe{=BoDwnr&`IZNXf!hbl9O)m7^M+7Oihssf7bk^?17fVL5C~Ur zWdF%Hmo{V8s-6$2YiqXHVfte-brMUSuT+a2iZlE1=Ups#S|F4dX)+H|+BZ=nKgjc~FKt zgLLQ6(?}|^Z9&GKy0iTVFD7#xe!&M!`yZ5Zr`XU2tqoHZFj$!p{t{-t5+mmAB5`g6kM1S$>D7Q(7dXKb>T6-e4X=sWF3GZQt&EZzf`_ng|47 za4gX*-Upt$uxVWj&u)kXH?a;g#6Sfv&@`lP{7*z^O)yAw#m2^?6H2v&6XsbVZ<&5f zL)nZV3*<$}*uAGaB>{OGIF7#gEXzU~QE$ERTz4y4Rzs-Y?08zicU3grAbB;B7 z;}U_SQ3@8~n&Rb-CM5YjdCy{iEPC~d#ouGzp0JmP`>o{LxwqbyBg;UROlBOwIiEOx z;A1qMb8p#^@cMmyH=WtXg+JX|l(u|#qiG4d?SKq^k_?tJH}fLG$B>@tZ0*@CS9T}_ zS<1JxVG!25cePC+>^FKO7556$?h!DpBqH4kr|@ofA==aFzEVbGy40vieeCJPKG98ACdE%s)9A!^Ir5P05wx7?( zw(aN;{n9KCT6!}?Ceh&WRWVvy{3VDIvelN4YT^lnG$rJN2+Q_MBbdG?il>6kkV0K}rH$?#og5Q2B`*mH8lb9hww0w=y3ah8=oo0Sw1KH4O> zjr2%)x*4st(+%c=CIZfbL88Rap0D3~0|h?fdh4F$_4;-^H4mGF9-_qaULv$=+1T`l zmT4-CaQ;dSg)&*KvQ7NbT1eQ~$2brw&FmgEjwTZ7QQd)p(2qvaV&GPVyV$41{D6>^ zZ|?SJ$O7X^VAInkxbn+2*d^t_D1f~ZOij%_N7Xn2G3`f!$%8o`^tTYM?c6IN=4@?! z8qj^!#0#fcPVG(C?51XCHw=+4gH%z>5;wErEsJz9sGPy_t+)GhL$ZvEtr->&q>^ti z@X~4+)BkuvRHg#)4-qD|HNLeT+mhk}+85w2=lo^?DlX4lRZ+eSQIRmI^G8c3A7^!{ z+r6;nkFS$1%;_|)_{daoRy~%2pZC+P2I?j_h3ySBPOBK0bY>mCm^OiCvaq9msH|MS zBk20?hnp=WCvR53;bwyn2uSe%U%U2Cl>Tq0n`zB+dt6SGZw8|8K{UwbB+|(w=k}u+ zM}^TyWpp_s4X0?L8%VG8B#XMVb#nD6hOuuyR-X$eLboJdG%kM&^@Aw~f?m%A?v?Y) zexKpp)>pVAmgGpCD+4vCX(1{Dq;Y^w2!jQ@Jz#qUA|0g^O+8;-hO||~99sWgWgjG5 zG;t)xuK~TmvhxE=ZUd(h{ppPxR_0J}#+{Apj$XAAtxra? z+|?as=D4}LcBygl`a~U*BcL+=hz==-iqVc)$kxo_a`Fzki$zUklQ>X}hZjE~I|RPF zJcS)c1vzkKzAR)95s%@$$s~Vqzhz@VmOmh%xHw&Li{~(u(*c-$Viz!jW=7IscA`mr z@ZrL@qKR;dAoBx+3$Jz8bw>_&SD+t`PM)53c3-<+cSh6BHQDrh)E9lJwfh%B~9v6D=>Pu!Ivg?hM-8gRR7 z!mPP z{b4(+l}1iXYd5&LL-Xg$8eHI%-~3B0tvNL@_!cfd8cg{o_v+jD-c3B-E8gpc-Wk4CBSK6fIp!&DV%Ymdvr**t zqtz#TvqnTu=v4Mc)Ha%Q(PIDBRuREP|CALCm+B0+HiM@RgHoIx+Nl$i<_xkL?#C0j zt@+9u!+i>Hx@5PC@I)(?@|1z;IpYMI;@|d|=!ntaM8lrOl9iqIJ{fV7-lSm^>xVCJ z#zNj9$3vh!CY|1TkYx7oR=Q=6H`*5D3A-0zR_sq1Sjc#&&0_o6cs2@MTM~WJR5CZP6KK(8QcVdVo zU&4CBH<*dA^J<7H&5APXstCn_B{#GB8Gk+uYne5HP%*Z(U_I9U`0&?g6jJ7cV#qhF8P#X|JT8tgaj4qn0DsBGqoyA%tnG4PSyT;E@CxvWj2SNYx z0Xy(e8~U3uu1ukn&2@A&qJc%LvbK%`@*pHMk_{XPcDJ z&Fc|HgWWkSU3^7tl2Us}*j;@FeX;LFWt?xyjZ37i43c-i_>0lS9p5EYk<9r{svzUx{TQ%BU_p&>D%6e2s2=@Rie$!~!2FdFR5mvQFy6xS zJR9Q91KyATgPWNZ{*+Vx(&53oQqa!9{1`*^Dn<#JxrrlgrFdc62W{ymamR#HqU3Z( z&Nb537NVHbH>IyH8P(FB$YP*jNIS?L=H(^f)=RN&UnW66H{x$T5TwT0QbTB;ixPKF zoq85&L}#62M51#M;1D;2b)5oPxcFUi-K{{k_0o?hsSNGt}$p5A;XcqwBZX+9Qy1h_>?pHNlTg6+Jo|>BTkB zGnHq_+Dt|D7fd?Eu1HL6NM^=~C}Zb=8u{9)gZ?VhI041CLK{9j9*v5$kCgP^bg}CYHK|Sj){?4jeKi?sB^g_$sbPVk^@$vVf%cuzgY`% z){7=IX-oAQJ#es$NLZ^I;H`1s2%ZecJ4GLV)0}o~QcC!U2ijn?)yf5t;FjZc$T`TL zTPkk<#lq6G4CICykTNxMLph8Ti0}wYYHZs~e z1({iE|LAQ{nM<|G#11#l^J=k)LJH=QChc6^?h{p~4r?^-!(PtxbTR}l{VGccXnqiU z53^RFwIn}{v+qP}nu3P`vaZa4O zpZ1MAA7;#_KHtV@JzM6N(Krtt#75shu~SbEzz;IDYPRX6^;+1PHCbkVqBbX6nr|?w zNVl-VyqhDUsW&>Y;l-OTdUb)@3sEtSoUP02laIXx?804>8Y};;&7E3_c|DL^q6}ef zR3x*gE>2y%+E`ED2@96JRI~L{^i_J>&xkZo5+|`2mCJB&b4k-xJy^Ml)n@JiPFcy? zd(>M44qF|qV;=_KB8GrssoWB0lhmc}V#M^W5uZk~8rwcI#Xqv^Ou)PhZLCW_b58Fb zi~Va-5X!E-GF0<<=P{*DSA4WOL%_JMYrFh# z^We#~`-A3gEAr2C>kTRhGi@7Tefe4sQJ;%lR>^C6#c_55x|aD-toEFcre9&#uTS<# zlWAm6w$F9w4ATO$yCWtu%GL(JoR7K%&+wco-)1U1VeK59*MLLcXl})wtd{oKh0GPC z&2xle?j0a*<5b>d*3hlrilbGnYn&HJDdT>;1+Vk*d)6BcvZlD2ezgx-R>thbOetIVJ7pX;CSyx2j>RFcfe$I4vQ*rHYy^|&$iF{goR*`y}+W^S9)?} zuy%K(Xl_y==5*O?7UoKdNubqlM_o0x^(y|^ihVe~2WgcFQ;KuWrdhAlp-y9<*<-_r zXnFwP*WAqwoWh1xkrE0$K~w-dc3bG$ayIxgVCN;UH?@Y@1|7jj|`fsCXwTh11g($pF%b*{6Ih`zW7M!FMmP$JTsBKNFzu^KEE}B;Y zwYBTkmP=sB>rJ-KhJ>Wx1{Diky_?zeWP0p@Etk1a1OIEnU|gB@9~L?S+E$TWF!Ulx z2x%HM#6{@tIux8grqGzbLG`grI;Q}sq!d_q(~!wDFC!r3m<$dzV9X!t)?w(XKolfy zBV_-+tXg@*VeF;)v9XlS)5rjU3uz4Q-<rz=x5gy_^|h6T1@Iiz@c(Qmy-)p2YHRUjy7 zW6k_wk1|GX@^!d)!Lv8bd9g#W1D)D0=P_Ct8%B*hqSL=)U*R?>tJWiO9^L}Djf)AY zV%X^Hi&lrwAsa%QQcWoJ89r{IE2>M-MjHuCp(jF>$d%s$BHiW9s-n%wT4A09g0jJu zyD*4km2t{n3a%rs`E8rCz={@xn-CeyEaVg(Xsy3R;{NTKH(+iG*c(fxv@r9~K)l&C z{}i>n6n9Jpj7;C~H>OTPA`75LS8_0O$iSJaEtdeE`gKIy))sEm+L_%CaV@%F%-G$7 zE3*QUo z6Ng=Gm~*0*$bUO;=LYXZ&XW&#bA(9lqJdz78pPlm0P06}Kx_p0Vb6*m6ZWrx%EnmpX1Z`oamXFEZNr(z2)MT@Gpr^OcrM1~1 zGtiz6>~fC1qUu;a#Q^3*Z-hpXX>dKG4H<`o|5%dp#JSO(zoiJ0l$;qz8cb+(s)pJv z>9K(94g*x*CEEbW`(;AN%w4Qmb+i6E2^Wrc9ZbMZ6J^DcI*0I?N->wv$9i&>+?#4G zM_~Q6qU3jFI7w(Oye{WkKFDz_SbO9lPNyEVsnT_7e&N6p^D*P22)dOjQ;A6OiW-iKCV z1=P*K?Fu`NQ0_m0MpNIK_f5idtG_hdg=I%asHcSNc_q-?Cv8_}c06u^zg_8@ z82>YWyRx0Vowc3Wf2z`*OdMT*#nJ!vtoxNO8`O4u_5V?bLdS61@RU>=B4(bnA6rJNWt8<%%+i^u&fTkKa!k zFQC9s*`S;y82-Yclnq@{6j}3k4nTR+Td7qfTv4^`FFb4)wTn^lkYy^Iu^+Uo$tP40 zn5-m1-V5hLFcYWD5wLh-^6^W`b<2<$ODMU>on`}}#2Avt87-vYESjb7$WzWhPE2YL zdHRT?VxCt69;o$DJUJT|Id10f_Vy6I)aV}5Let9!{drEvv>D&K!=q5V*eH?;7&BLY zvm{EICMYBhkXj?{O(EU2><}sY&YLrkw=<3PIaRMnS;`@=Rw@j19KRcFkB10ea*)zI zspNhSp_Zktxj$)qr^3Y~S+o=HCBWO1C@Q)||e+IYg+MNY*+EbeR8zo7>4vAA`Ho z)1KvHNOeyu=2j;b?AiVS4$P^mJzG2H&!d68SWE7;Eg$U8Z}_YD9NvlE4U{9;9Vq?* zIv$$}%SwrJr3b%@1e)0>V+bmQnGEuXEroDK&5t6CRH}5JPCdgvCm}~ZV-1%E$`&8_ zR@IfDc+4)gW}f(buF-E!+yQH@Is(afk!5#m%pi8#xY%U;0&s5cZfgD;v%gZB+ zdr%3q@FbI1&juq|Q7m!kEU}Bs%Y7PBN)J#N!Cbn@50m%XA8(iON2NgznZnp^$%h4{ z@crXM?%6yybXu_*okJt1!JKIqJm0#a->4+c(O_4|q-sC|a3~ZUr9#0l5Ahr^E>c%S z76e4yP;=@=p|DJIHO-7-UWKt${Giyw#H)X-s9Rnovq51uxCKEK9m)jTb;cQYEVc17 z`O}ZZB8mN=uRX(*Lfr~HxK3zC4=bB@wwlZDQ1>ObX0Hu)r-LplbetWI$+B~H>_R(HY?zvbG6-qk?s^42n>sQK%q@vIz& z&|ct2?8DPVH5_O;1|P|79C*}}zNrv}g9b&=FH!T9!o}b^CN>pWCZKXo~LKuw$oEr&FxnL%lw>BxXd zLDP8*IZq(-ImIn^2JkZgTq&Q|TdLLcmgyruo-4-~IWzYYa&ML4+Gb%lu3Ok$72D;I zCe_RgB9d)%^dhvr9EasNWq>tJMuxTS@{H}FFIzJQMnN6qHYZatFtbh8f%hUlijBz+ z)SyKIF9#B)y0DRG(%{+v$Af-!)p8WI?O~wmRhS00?$C9x<^h1AnYBqaq53li+C#&YRcs1*lcp+CH#U zDYR)V^a#lbUS*boQ)EHm?}xy#M)X9GzbmCQ{*dqe89YFX!{-`xtQ}qG1`8d2xI#oD zI>6qFiQk9MQvgfr9dtl^uU4jIZ2G?5|56}ZDhKhlQ)ADbGjLvYLC(4+)RJ7&!3d&H z6JV%zpYeF1QE>k|lE zFLyZ5FCA@9T4ak-&;WB{75AQxBZ+zz2+K9oU=Z!mWKs50Zjc9^$Y^Y645HnWTh$Lb zIb~n0sy)Bi#+Pj?6ej)=#yvZd;)KSKqcZB;@Drkt$+!@-N)!x7REqXso*3S^3K=84 z+B80vkg8+~Qv)k^^%p~Go6TW716uA7YS;rVb$Dp)(DmWY?5yDos3vYNcHr{N78PUK zTnD^i?Z>?U$HTuQQnig7Pm1J(hhI|TdmrD$=M!z)@Pt>NxOGiNp_QGRR$lSYc{!_B z37a~^m=$gp^R>XaY296nvPEK~e&FzUu2YBn<>}w6XI~_x`s_RIeW0z{12;ZlbGWHd z{1EXcri}dKw7?k|%EtEcbz_3m+&r>Z!idc%?OLBa&RNkWGl4rvu4}IBR%owoap7}v z>tw0z^}1A2ZCo+3dO1cv*7QSzwKmi^4SaWd0)v~rsIers!hexK(L1c$lr=*Z?+3;bT>HOFvdUkTD6c-Qe(S_vVg(4qD8&?=?T-?vhv<(rqCF-z!$|!hU0tzuDGLx zuQNn#o6580k3RdyyAR-u`_*=e2W$vark3(0*lSyZsDJY527A~44( zk#EEiTOZx&Pb--?n)&a9w{yyboAKZ4@;dzgY{{;w_P?Aq{}r74Kj}!d|5~!+{w~?y zi{loQZC^}!7lD|TeJ9opC$azq=w}gaoDxZ*i;~={e?DUD5X)PPsb-gtw|HKjH{)x> z-_h2T#3zh4>q1QmCauQC1qb~k5ujYF9GkgFqN0XCeQDdW6t|SPf6A#nh9L# z)8l3u+xGqjZo#E)wXgdEeT4-=p5vC=F;9>-B8p(;`rq)8IT zB{$!IL4py;hlZlj}XJw@w-ebsSSv z_F%8e0y;mXmt=1U#98B04ijc4iNmEA$-uaI&&8Y5)F;@r`Rw9Ur=gwCf&k3iu##?> z%B7?2F2Jtv%xpvsCO$=K=iJ%q(3exN*OYJo)rc@fX0!(DE7(fnI65Ct6tL7rXJ2%` z#f5yQj}rlN0Z2tBmo=G!f|*sMVeD@_r!DEz2)YyCTO34NFDnj`tE#5x!!P+sZRVy& zC}cG~|J)c@ri%oFUv2Nxh|Bxq*eT#-63fnLQ4gSOkIgRG7NsA7QgJA~yVBLnz@#Th7v4Bw&ng<4YqT#BDz!9|sX-;5Sv!bXX zl>r!|D-%$DrmB>7hQYRO9i~)+p0r>{2Z!G8HbILZ#UL_`A6Ltn)WP^lv18CUv52>6MnTaY(g&GM7e~l+F<2Fe7>jHF z9IZiAAEfoqrR(i;?*XnA00lL!VxMHVc7QBlw|UovQf1G9Dmj^1%VH#Ql4f!_QCBHi z_K3o_gyqC$rgXF{2^nv3B?V$3&D@-CZ@>Iu6H}elGM4V|;6b2^q$xKhCxVa(R(9Af zT&SWXkN{SEpsk46OW(4<2hC|A^jYzzD>|#Y`y1l0C>6Rg*JCKU4Jqy=6q}v2HdAqf zv$5|N>*nCH+KAm#-!{Q@`&yGsi$Jj2l=Dn`_2N#i6!0P`OLV;v_88iuw`|vb)0x$c zBrF0ih=2Z}OyEzK62t0Kz>Z!QBquLZ|5deFDUQMQ>eswc2xU+7+I)SYK&H%r26KJh zJ=8Ex`R<20XFfrr3zmG5H6Ovhg5KHjbI7o)=hN#uB<;2g0yi2y=U#%oVRn7+tCOc2 zzbe1CR?$(ocVbnt%%>+iyu@C2`!OS-1bNn-12ZZwrxmSDSE|-DFUbu=GdQ)9bbJ?& z`}HoS&eYH&fjIHp&1ZuA9RwW;;JlJStm!#@-UkQg1+iL2C0u5W;|Z2?fj`V2cxFzw z6e~d%Yu4bv-9ErUoc-5#$IgS;h|3!A9^qrE!q;j-RUqpO&%2O|dPgWd)Tv16>Vdy; zL^=d6%xxEY*h!&t>2$0!@?K}J&2hiy_oR{Y34bg(bZ^Wi&uM=pg_9r{o{sjwArqW; zC`((9f7gND)7^S96%0M?H@4%}Cf?RGxwn=6**Eb++bVsvnrJB-gj@+w8INW9f5pn- zwX?ofoMJ)u?5w;c57Z3os^pJNo)4*HW&A3yehdLF2uL+b@bo>qodbRMNO1+W@6rq~ut+!SwI+iY zacsy?FUQBV51Z$FmI!l(8RvLLm=;wXYxU5i%hY*EmV31g>S<-NTo(!2<@`y)NxEZ8V@H1bj(?oX2qXq zXH`!u%Dy}77?bm9h(=SqQc=kU&vVuOQ6|lc1jYD)-H3+ivW^6yJ@LatA{+HSLAgce zsh8af=4~soseZ@c*ZreRw?|1r(NNuSY=Y6#lFkX`@k+gNz62mNS(TAZ9xF=*bRJ6Nn5fWwN+)u1{Q%L+;@UbC9{H-!jG!opq z;nzq)`kD7}_HYJim03|qlGRFS@?IoRj@$gCWrp<28K+z#FV8nwG|39emNB7-rI18U z4($7*w$?~OZJ2OS;=#eez`;UatniKK!1%w)(}wZAuY&n3OJtLC--_VZMlFJG*VW6~ zkpcIE!^guvU5otk*HAu(yMb0sMY32Vr*ELNIe*XYCPwboLPm##o0>0kxN#q4bGdmx zM|N+5+@1bul1McVi7F*1)7?InEl>!*OH2|^>RC^Gj;sxO8q2JhWyAVf99$!T3{WSC znZd>Py9^g(7m96PE367hm=4m-Fedu}p+5?8HhxD2%8T=oJ$E%s+Pq0q!}3k8^ars1 zMGkZ8XRK+!C^$Th3RXWev6WJqE2~w6-}EeT0_39uYOxl|JO znPA;+Z?2tGs!C^(D$rdH)kot5ckC~Sm?Sn2rHao%P0^KM(xbwTJbUf>B@ojzM>5PqMoC7VQvs=75GXxu$B9#gt*z7A-30 zUoO%ZUrk(g7H3Q+VF*}$fuk=DVXMLWoMBW@p8+7SDU*N2j}(Li&Y3dqq70e46x1rX zLE2UGbcvIWZqAwT2FaP0^ZQP7{dAtywz$q+N|5JTk;r zV>>gJj(Bh5uKiljc%_Z5hlha0fbfy0u+bLZfb+L$z6D`Z$1n4e9-Jil`(Y~0dn>rr zd07kIXrUjcW8amWmD#^~9%@`Ot_mj-34ew|w>bmz3l>FRAcbk^+<-yC

SwYqn!>KT;=NZ^XA&M8}BF6y|uT~%-Pg} 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

WI0 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

(QxPM_z1hK{OsiJotS1)?2xO@TIU zZ1|nY`IAQd)MiP;zeHc6WnGmao^i$sj>+{+k};J6bpkxugbnBtdi>#6Wzvk|d;7Nb zX?qWbg-dXHN5zJE{g%J_;w6=-nT?9{3R*SMBJQJfKUVd5H4Iq)qSgj3EM#6gC-!4V z1yhJFxbmj6KBZOxI~D$d({hMq|85@U!0jf?Zo6F-Ml&!>KlNKU}*I? zVZKP3=Hni>1+=;Hz@!DXq6Kbr8Qd;N6K+lU*$tF)0K-C5xyWWA=HetpIZq|W0<}d` zyS8@qrCh-u8Rv}G!q@zENw8JM2?}Var)r*w^ttW|xCYqTt3Um%OKlZ!`y8Ajq33uJ zc3Jw7bV{7${Lv$_FV!ZGn7gY|4b9lZ^<Q&v51oALC zMMC>RP0t{d3Eo2bIu*J-A)$YiU|{w9OLds*mo{Y=a`P+wYDg{SPWKrCQ|{4g3WnI~ zU@*E!ttq1(NX^ThVW`wE7G5xwt9M)l9bNIT*`kG9-6_}Y*!xE;E?^ri6jn6zm4myC z?$?wWgqZt_rF>T&&LrC47D`gp;I3iYGF(T9lFP}d6v1ZmFZJo|!O=?JYE?F;!(V@} zt}4K@l3Vp$$-5TKN(%*xup+rLztSDdlF+tmh63J?V!rdpw>dUo+Ulk02U3(>iC)W# zZiDSM;YP^n9pYK$uoTRnH2%(>Qg3`pIr$lKS$CSTPi4$WQb(rmdqKLcC9hEya00IV zO18FWXnRMsSq(%db@%>*N2F~H0~(jKKBowU%oC`K#O9FJvLpAsrNa?xgfkEzT#@md^%r?JBIiV zNVl%eLDMTYmFl*r(#J2+-4Lp1pFo=j!%9`!JlU>Dp9dt9mz*ibyR=Igd#o_FmuBRD{uVYU14M z>*fLL7ilP9qCItw^TxG3EGy7PpEaB-*TzF_-wv$Q6g*BNM>}rJftZV97Hrf4MT{p& z3=;oi@RGVJh0Oq};7gm*T&Gc~>KE2Ryu|f+m(r+8TZ!4-aLGezOuOz>cu&F}!^I^_(=>Ll}W0xV(EdxnXL7=4)|=_JW< zI|=m?4}=RwHXugoS)nI?9PSB@y{1NGnlIr``Is2DP=F_{%HYBN%P?4ZA379Mz}~7gjOytn+29wUR9@rb<}%Y41GtiS%TD(PO_KZY z3DT9zDX!Qwap5<$ybtn#-h%y=vKfDNA}}^tS22wQr;sg)3r4l7Plzee*5M${piB4G zL7C)Q@k?28uw-8ly&n;L_}V>)z4TfGMfB$p^p(0b-oM^JiVBd##=+cNAjf$9Ztc^o zQ2$8m`ab-vk2f?5E8_6I9;CjLM{U04dqZ1mS{HK{b0bGvE9bx5){e9mPTvV2|GHXD zYh>>D-N!RDrWG=GG<5iup2hT?9gS=qOiaGt_{S$i()w;f#x{=TCg#RQ0`4LX`i6h~ z(7!$wF}M0}i$|Br`u8|U>` zEXVGHqbpV(kZbUI3QYRSoyu^4U_!GVB!$cb0(@bG!36ZOUiV0mTz#qV5@fC_g&4^K z2$Y1>_yTi~X$mp&RD{rjhbD7c^Y-kXw42DRn40tolcgp{km*Kh*6xEgd+ruJ&f=ET z7*lt-76uWoH5O`H4c06pb7MJ!G!+cWM!gkxKd`5O#PypyR8 zE=R}XD=2Lw+>3WbIPnQY>~}?}ub)|&f!3Vr^K;yU##$8U4AO@2By~eq?Ro_t-Vw#S&K)pZo70``KB)71x=H>n<{5 z1GV9B1&YGB=8N}hD>Uwu26xO>?y$p58%A51wT7ZG3C-r^A>mlnjK$*-dVWu)PF23H z4_3V{2gNZ5!SEaWst5;(7E9h^H=*N6(qUkz?CK6smd7YN)-&;GqhogV{2_I+ML;>N z4_KN%D&wL|9bq=DmP^UeZ2R%v#o9Mp_yt^cyUFm7`GSf63O7*An_Bl#ze6app5$+O zzIZ1RB&%PiEvS66T+G>hU4Un8go{@s8_zC!D;oc4!0V45+|krPUJFsMxRn*RQuG8jAY~yy z7R_z`6AM*KvNr^RA5tz&uO4^{i0z;cz`vpDEKA$1oTsL1s* ze%yKoj3sQ|#_eyTWpvOx<-zuET#=L^h>utUvf2Z3^Y~>Dy^ujvno@Y0BN#!~>=TLN z6Hx8z?*(B@V}Hpz(p_F(+>Uq9A(pDfZd6t4YA{G8tVe( z>Mss`|Cd{&8ad-|((U=}ZQRANjL5v05ALAV1fw3~fLs zR#_hT2Q&Fid~Up$7!!$!SqOtshop;X*eg7b|Eu7~F0loH0n^P7FS7kKbEu(E^8D3< zjmPt+EXLz=ueZ+^%3u0YD`uig0#hP@t4wRL%lUfxGa}`v?A3#@GIq}Afz2`+=*@O( zeHg(`N!vLp2@DF*NQyT zNzdZwqliC1RWd>)v#Fpim*4B8^;q#t0s93;M#I6&p@FdcPL?lG5CwwZ$atA)yvu0l z4(cbp<1>ik4dhUvIg1NvZ3Y(E5vH!$V+gbr`NKRLxDwZDz!EI@MWDHtPv4!AqVj1a zWy4~7+m7A{s64pBfe$w~wZ4kv$n2Ab<=MfHkFi!hev{6_OfQ>5`P#Ii1M925YC+69 z7=($;-=jfqqU8Bh`Ry98vL!P5Ns9gWDQCf^uj)VU#P0y%97 zviT5-QtUUYo;T6k2?tmB1ye_?810Ukc|qo z*im1;Gtrg8IS^~#J{N^ijTLp$3j&TEVye#kDV=?Qt#_ehb;9u-x@h!zABu93BTU={ zB>dl+0a9r@nn3#th=1rHpwZnU%>XjCns$*N2|xap%Xqmp7DM#ik~W0_01*G**&zSq zkX+=NANjyj$0C}{6Gsjll!dKI{njmcU^A#3V(Aahh+oW=u~zg^|p zLs+4==2gvSgy~n(mYk3*U5psAQ${XtPLW4GyBUwr!+JsSmsOyM%L0X=1rp# zt27Z4+2)c+IFmL!}!%F>OgIB z+Uz4ZZoF?!8OL^T=AgV8R?F+00zF?_qr|ObdC=KqEVtNb;>@Wqg- zV#`0yc2j=SAWe;owAuLJR+uf7LZf0Od?#`#6O?ArNVfcH!?vS(^#k_ zeoB2WT*2{GEppb{S~Y=A8^jSgkgiw{oR)$u!tvcrweNuOK)&}5UFjNditIX1w-?}* z7`k>eFVTcV(6KBrdqF|9%E%_9-2Qut%{20>VQnf-PTWJ9`MPN;PlS?Fj<1!|1nV9a zE>1^3i3KE0{c48QBQOa{XJ86(<(KP={!%KKJqQOPWc-SPo}Omk(wB09RWEn3>}(~O zsH3@~X3VNVsN4_d=i~7j6{~T&g!~#zv-rdepVq!&)5%^5BH{&$G^y~%n-am4;{?S= zsacUBoT&Cd@~>;gPw_EzKg;uj(uDqDOqClu1xn})^jkDan1OH~-jbB#o@>n4)3@Rj zp0o`+d$8HjL1`kFKh$-Kp0k%l+wkv1yi&5P;swEvM?hlo%&{Ss_^mPNS3&*~6;hnF zVsD6mPPZrnIEXwj;0_o~;0A$(7(NSrvWQ;P7uv+n5Uw%nzu`2#1m=nKRA+x_viULd z7sWG_C6~5iQ}EQ$(!{F(mu1+Pldu;)K}5{HE^t{a0xbyNU7zRaSg(DDD4z4H1bCV~ z*9rN=^8omv_gcmBy3R(kd6Zhp5ZaF%^F$({42&M5&yh+w%%*|n3Fg=dtZ^>9pU`&- zN^f(C?EgqKvo!oE{kv4q2*hz2{8DANP&j(!WPnCy*SdF=d~Zv0?;M3jl1*2vzAvYp z$q#4BGH~dzvEmPs!xaSPtyyuE4=%N+;|r{P7bRPwe{?_ot^kCsN+9wH%`Uy#PtxdK zFF0zqFL)DIh=kzE0QY1UA64BRvyZ$$dD2T>wnE*jlMXi%&Q~lMEzSkLS~F?7oy@UO zDy6v)qZQGEwc!xN6?sS#QF1>V89?p#lpXjF3F2`O1Rg$3rc}|lK+c}SHTpiwW1jZV zh4uRr&S%&e9Am>y_j{=YP!EeIdxyYfMIvRHTL5-oF9{ZJ5x>{W@H43Pw|K3$aQ{!{ z;_Ir~JII@8c?g|8<5`y7_(6RS)mk>GxetO9{n=IKKo7quRdTLl;giB``op|MqH{!y4I+5Ur={0o4$ zzX2He)24n@VCA}DAwR#l2wnh>8o5x8Ea|o)hz~*`Rz*FYSzD@Js-EsKi&az<_iYSn z+FKka>-41JD%#gi_7j9d*-Y81m;cg;c3AJ6nOlQ^eo;ChIPKcoyr1~&niB7t{$x!Kc`OlSZ==*^Fc)aC4T z2lIb@6{j&@$p5e{byVlWXo(Nz+pJ7uHds%CA#5qMEj%^PY(n(#P@hh_{fbwt!_9E_ z+g%Mz3cF0@G-0qUYO$UlJ1WR<;VCasRGDhTHO&`iv^`r5MCek)q*8}UPHPHDi->4x zwk~)V*~yqW11yGqEir@Ud^%_{&r;t{PLfPl-(-;in*Pz0o6*LQu3r`|2GQ8jKQK!2 zWdh7q7TBY#S}|1Rv?ex^qE$1Zs?}`_g-t62B5ZSzOid3J*@>X-r?;_$q&sn{IYrVi z2i(p5GlVL+HH)#KtYJO=kI>o}mb<54WD?VlOd=H!F+a@j82rH~rJ)fA@8eOZ2*sia z2yuVm?Y5(2O1Q#s9=|Is0IEZFMvO%-^}ufndW&9BPN77GVQ@^$xL36#%-V6C~!=b zddh>Om`m4kx&6jkV>_TcXstKDGaC51kZIDF8p=-Ihe-V9 zlNhXgFeCz{2(P?2R<4-3$>t+(yaG+4c~w!U8zv{YTu4mVsObIdPM)DfU-z%Ekcq}A zZC4!M6-=5})Ay^_>(|}ucPwLS3o5nopSXj^?`o))$8hRflOn45a|9MR46>i+SR9Iz zG!M$y-DRU2cZUq9CygixM*u7C2MQ5M7Zik*v<{q0F>Z&!@Ta*hK#pk!uj=oEa|F5e zdje23+@%2tI+2|Bt)m1La0G#2_+B1>>%Y(hBdDt5~R1x03KD*(l5oz#dl=-3t zj+|>@@Web}C_H8Z&jyxbT)e~z1MW9^$b#K2u!gXQ?IZOtT2{bpIJrc^oZ~6Chrx{I zWl_=W!>Bx8IwM(3@Q}22HQ5SWoY*Fo5d=t?Asn^fd=>Y3OO5bs%s zlwYvl5Z3-U0vi&^aTdCPc};es9tOp42#UH!%$=VliKt&u=T>I`pM|`~q5d{%KO=n7 zg?rHvK)C?EAhMtzX2>MyU=LYuEC`jr;TWm^!41orGtg(hpwHyeV^%p( z8eXnll&&*$d1#Bf7)y1w;~oZzvaEa`@f%$!9-A!AYv2?zr?*zE-wEr?qDp}fM2_tjwoBpwxYc1TB z7t`|B%PW)1{4eZ4G z%GhugWpB7Bk45QfYI4` zsx&2Hj7qpwTjj{w*oNXx2KdT=I8qD!3gS7vneSF7&uEd+(?K?QU8TxU@;h|ueCOdIm>BCZm|Th*iS??|_C0?;9vs&dmvjqH2MtaE2QKg?cR5nf+Gi;M?>uMxJ1c0u&aq}`ui@I+SvEE8@J79ZwV$O%?OKV?iPX*1G=Ipd(Y@gks z;@S!6;qLq|Il%eu0c{lg>R+o1_(BxVU|y~tzvr?0bUU$5s$t>9r19K-*-_a2iu2C8 z)GW~p(M`DDY4gbiWq6d zfkxzbSJVYs_QXWL$8>{$pjC70^~-tlXN`uDsaGI%<8J{n8r`{D{k(>AelFbKn}sBt ztdlEkz(yvO&t6DUWlD0vEX#NXFNxtmn}yZHKE23pl&7^$R4U$R+&p~_R<7_FR^J|Z z3O(_odwfuUFT~jL!RPIa8c*8mXqM}0MXb$n)|T{2V~BhTNUS$)kqz0hWH2H zyd^vNky!^s#{16Wm!JJ{g?)Fbu6?nu&!fkX?FQCw*2d+BufNURM2pK6c=~2i=HE9I zb^jCWu=*<&!kAXv#>m*s*ytYsB5uR)V66XNc@ae7f8APi(YN}qZ-^S(eorNEaQ`Q= zG^wnsD*YvLJMQ-E0$7lx#8yfj0Y>YgT?-%-u(0ES$D6hEPb}Nn9bt1F^X(LYHL1WY zN_O3^MbtDUhaYKFf1^m(SHJS=6(=mA7+&Qc<7wB`lx^Fqj|=wSpAUCH0ATjYqLg!( z(Wl_}+5xE8%j7aAZeVE|oe>F5BL-q&`YE%Ux-*-rLc z%4Mgzleg;FLhAl21Fyjm&4^dAm!n4}YY0erv|97w6V!m=SsRsDc1~_=cZVAr4?aAG z)2%`-zU8WeTQw8$roChNNCJ*JsVOR{)Uv|ct^PnxGnZfS;S2_d{80)wBs#`=@}=JF zJ(u|4-PsTN2$12X_fv)P5xQ=vBC+_1*c2 zE1m7Xc#bC8*xff2s@A)0LM~J@q0dEh@IG`qg@L9ECi$Q*Fk4@ML6YZsn5dTKd!T91 z$poA)k2q$2!3_6QxJl<4CJZWhnZRfTR`fFN6ioMIu1#Q_43>bxlGg#CHd{&>Gui>$ zVzdU1V^)JpecHlh(h`|9=bNJ`loXp7MccOcIDgqoRe2j)ZKeK6>W)%ScF~ud$8nNq zh@Q;E(Y)4X6r}}R1UWUu{XM@mDKp<>Td-Enw}uk&b;mYSEN!bs^ZetFW2THtt-02c zE`QG2!@$Eq5|TDUb5b2sGkpq5yJi|=-YU>EW=Vyd)yktR6T1&;(%t1@Jy;n!!-qL6 zF>@qaaXHm`0?plGp~z@@Ah}a{q6JOqSCz7GsS^u6%=;P3&`%%a#@hsc?y@ajRrzYy zf~;GR%PG7GTL3*%0|)|p+`LiZKwH9iF&g|=Bw7KdADE(K+7e(e*E1t6Zv?U~+$=+q zZXn<)Z!`SM_{^qb_?PyogWHz}+Db6aTLC0=8_sZxfZGLG5tx3uxwzqtLYKLZa!j*` z+Wz);vsV;eY~b6lQ4W49Xc&8l(=?0|C{DW+usaO^61)NDyDc)<5Pj!6G-2PG>WXC7 z$Gs3ZO75m{uKb_E6T$(c+@E>8`erAycGvhiN94nrX+yU6YLnZuBeTs10l0RzJby?M ztmC|7>#Iy59{ROgX6&HWpBu7xUF!;HZ|`Rvy_Lf>LzzU=E7~t}Z$!j4yqw<5)0feY zIMVF{fO$x(XrJ3$VF;h=1sX88-EeGYD8$XWP6&5>N>5jZ*WrW3pY7z*W+8WH@o)%o z)dOa5!NWq(_j(bqsTni#>L`@Xyw8oW&JD=Hn+dlNy8=jv(|s$MguG*?Yfk=Hvh=h5 zXcU?us!EEj`oV-yiQGun)Pi)9!4K!4m$IKtJF70gvMHvJ|{PAcY( z=1#T_{~~ePmf!k1cY<`iNr$v$CGak=d!2oB596chna&p#0m*W-tQ+8H{?KZFPkJ^|JSgVc7+(=E9W*oKF3q3Gb ztBr;_%YB{zobs{-E@QV1sKtStu(VKyB&jAjhPz>oy8t4UvYBuDC3fdoA;oxLB~7d#2zLu^6wpyTv7kD%sr;W|k$G#! zH-VDe@M$yQ9CjTFw~YzF>O-gR&sT>;fz@r>NeI5<1%$+fmtz5mV8O|BuD(UNHk6DF z7ZyDFE4Lo|dZ^Fhc!pn#b`yf!S0V?)P1~{y(n-Mk894yM7Un*uh`+ z^vgI!b7Eoyre^M&yl2mhsYV;T!<&VaNioCuSizrTl-*KpOKkHiy}W1Ag4)IMrmcLo zyW?J?T|3WS!H7<&7$*IX}?zLt`BXtzw;xe@01b0p4cu z8{caUEdk!a7^hBnoKaSyO~R2iRv{s;3v0;8X^^1j0rU6lXX=?|dq~KYByjvlk?U91 z8Xxb&sxq7mlAuUSiyEyrmWhY@!bpANa_6$q?^*U8raKjnDBjHLLA1;imFy-@4FuPO z!Sm7U_-!haopIaO>l089IRb&nP$-~!SBCmh1b&DcrT}4K#|I~!u6UOp*x9XApUdBa ztGwS(s&qSH>|N;l$ST1s##n<y$AS)}dV!<~)Wf!L#0deNDc1p05iu9iQyH46P(@^O33}uu{de zncFwf_<+~3#XvxUW=?3f948O%Rb&4d(fNRAWS5mkLF|y3M{nKXRTjIwvqkCHk1rWP z;It$?t+G8lMA2rh=wEhs%RZIKd}yt0)*bD!!l0ycwDGDxtU1nwe3=sNLDIhj)I0vI zDlL{YP}v!cQ>Dvm4MGcUtEbg~n=T_(pVHlvMP8;>7hy&BQ_qWgsPNBDUl_eK12Q;e zuC(8R=(<>6S1SMurx=X9w+PmNR}0Suy9CermPYqYGgN9hsVi-;!`#VJ?WiMzW{chc zng<`WrJ<+GRMdCY4K~E3$%G} z!4yC~Yp{f~Zl^DW2l@0KhzgcUT44&Gi0Ghq?d^^H9DSHUhy;-X?C0*m(aS+p;$QV$}Z@(B>S?|D8q$FZ^X@y_n=ln+tBTt6)b+CXnY(8fsli zZVo!vxU;C`2JoFn*Sh!{J$QF=|Myl5=`R=(h|1Mk*3hMWNAA%X*#fQlM;E(t> zFV_E2d;F`$&~`-nZl}rcv>UC?ums^r^!n>{n1=zX6v@5uLy@0?S(6jwtUF1!Sh?(* zoXTb0jPc6TX4$unMlnJvn__o53}Jlv?_cS0>^ZVzu>9fc4@F~VKR$os`R=G)c}`u{ z_h#A4>A&?A>MixZqtKvh#@cVi3yFYqpva$N2!{YkDKyrhAvvTg zA_y}_VQi%`ZJ;=!tJwpnCNgWmi>je`mg{8(!G#zIFV;k4%cgBDIxlaspjOs;%eHNk zA@0~yt-rN@1O%IZS^@>uuxinJF77$JJZ_I{uWm77XTmXqZ6_6FD7Zw-)4WSIl-Z=D zPT%URGjk+OGF9^Iy{2k|WffM>HAe&-W#1;-$YL7H^Q_E3zK7m^07ce96Z?vim`bz1Xv#kWpXkJv!Pnsl6|PZBD~!2eyGsA^tH-fq0~Gnw^(b6SubeJ55gc_k-%Lu)%Mis z&u%kX?!*K27-)q9!)mA4PmhhpwkCwYWHdu*m!C!z+1_l8dLZcciRM`PR3H3w&GV+- zPc44OatYo!3!MlG6ww)wUeOfnI#ZMPix`H4XGw0L9#FC$^3tMKUSUTJOZArA(hUZT z*=aAfXU3PhnorX%(7K=8qy$a1R9CN7eF4CBt*m0JPwL}M175CjOs(wvu2Z3A~35ffqLSJc{& zXw=*HPI09DwTWNGUQv9JJP9yDZ>1x|IaA>YeLf@Q%$ZFl>93j^GX{ktV&V=*Zet5_ z$-SK6J9vMw0$G3-eCC)x%_?1Oj;H`~*7S>}`vi_}NBNWB7XBKm00hDvvDGoPVtpg# z+E@1oe}H4}raXI4gi+97)G6}^2Hi7b6ZfoV3EEKF^kL;>=?nv_X$q=Gr|5o*~!jo`@MHI`eZ zaac^|PZ!q8UC~Autcyd#110vD6ORxUZg$uyn1L5In->BKT=plzda@I62iuljW`@)& zHDa4!IB31v0LD~EE%FH4$MJOrD zZ1v7PNg&&EAYXZ>x}f}W|B+aNOUEaC8DH>}@-XP54bmnO@SRv*ANC7OR@@dr>`(`^ z=C~m;!6^AQvVQIjU2HjT9}o8(&fK;giaCI8LA`8R*feI3vWY$(fNDR%&0$yr{!tq| zW8Ca-VGp3U3e8w*rXM`Eza*^qi2;WY|7hv)KL0H)cBIQ{zw6tpn($uN(F zQYQshG<(S<>vArX0@yFFWi%@_eJ`(Gn6xOYBciA&!XOP zRB{T@Zzs1@s&dQx={1Ql=gx@2lwp9~$@|-{8qx%R){1KYWC^(bRgl;dPZ*<+MmqcH zR&EgeT#vtxVRa@ggaGbrlSIB+wWL?Giw2W!aY;JX{Vn0L1~V z;~vnh>h&?+7Rqt~t9fOBVQaaCc3^aoxh$OWjRy~5r*-w?Iw|%+P2&SP#s@c_8k1{( z9}=XQOvp_8y)e8$|3eo4&&BcIPSyX%3aL_BcbHc})()p0cM*afajo2GwdnuM8 ziIusKl0RdqulzpCq^9E{5X)e_pr{pdrEeWMPw5KCkmGVhOwf1{_W$GT9Ro9am+iss z*tTusjcwa}W81dTv27b2b;q`CcZ^Qb!Q^OW&hLNc+FVK z$^+A-Plw&N7D+1OVTUx(_AiHEc|g@SDn>JYe{9296$B&)sL&E#%=d5Lq@5POZsPA z#7tZ2jh~oXyVlQ}p7n8Rkgr#qN~c$6`Ac3SV5do;Aqa)-{(;V zpjM4&=U(es`+5&IWc+?025twadtS{JLFlMquNI*43?Ebu*L+<1;}2Gz{o*>s`h1l{8JwbCCMxMzApYGqjc<`oGY_L z=#G~6GU06DPr*P)CgTv|1Z^3&%gKH>1C1I2`ha*d`(+2wVk)P1o$(;*ot z;gUDnJan0IP{E-E9i`OtE9}1${Gy5c@RCoUe*b*<|L1`H|8W&#WB+fX>GGtc>HKJH zT`-MVvC|pK2=-=bDNrfxlPa#KFZ{{rdsIOm4zGCb+k6JUAp_{m;9`K%hT<^W+)^Xz z$9*_k74n;1i?)ibH(dz1X)G^gR4na&9|l&wL=(>I9l``DaZjeTF>`$tGY0%gS=uf^ z^?!O%JDQpGuO=ZO-!ddRmqm&uA)BN+-7F&+ooC4CS3kVj#ema)adebmw4d#xMuL2{Ao#Bb*v{xs5=@8SQz5VfR?rWek6MXC?}9KI_EC&bt!5n5aMs#i z^Lo;i!BIMIzl++KuRHYvY%XO3&n!&N>DgROWo^vfYW|Xl9xl65L40;J;(q2|O#dNH z-CY>P?d@D7Y&;z-ludyq|2mKsbscxq&%POXyChjch9(ujfPUc)Ry z6&QNy9Ev12Y3?L>iclhV%@+ZW8#M#vtG9S6f?mY~w-U1{XjQJ~o1p5Q_vWYG{J8 zB}Lk(JlZh$K)v8j2oPsV>Z?63Gxg56HhNBDQCJ0LW^_{rU%}*(3-mONb&J#_;GlE- zp6gCrWaq?URa=%GcU-&#xR@CyG*baymM~Y#S&?nZU1nYL^4eaj=8`4!Y@-aayr&@X z%0~G?x9-gST;_Qaw>f5MI^3uu5=k#50~hDFhzzA3`MKbhB~@{gMYhaDmz4hYRjN|5 z69kkoRLJl-BY{t9WQI0MI*hccZ8jsOsg$d9%dnxg3v1)LN{DhLgntx`v(Xg4wXKa; zVIwzME3h8!8|!SvRxkTT;#CmgvYeZiMhFD2^Eik%2Xx09XNAlw+Rbu}b~J$(kFuzW zOe_w3#ioi=^X(yX%_X2KXnX?}#tKH>;SrsVh6!CW`yg?#dE9I2n@WSV$~a+?8L0kMoSpgON;hdP>*Y?F(wVC#b7$$M z+OZw#OSHHy+>?|uA1Ip?NZw}D-=R1#6nC!GWnq84=Vgo>AM&dn-CxS@_#LQ2@u<7$ zH*MlIW$i<+riAZXza45%sa9>|p#q#0TYe1|oLX91OJqb(&vFp)qx%C94JIgZ*L#qK z4=UXXQdd-`kh=r%(}(To$DB^$|oU^mvV(S$rY(eYM+(#F2uHuxD96243zvL ze0N@=ItX}^;r2u*O82en_hn?~RYH_M9h-$MKS;|}`p$KPIPp*7!HwPlB$kKBUZv{w z^!WD(VK2hWC9xNX-{Po!3jkjG%-bTrB&)W19Sk^2TakSO_!2o9-L)~>vEo>64WK}=X3wKVsYzT$Pv;=s+B6LNwctZ5J z_t&VHr@MYcSi-5CA@L7U!ZFKp%jrvoHD|_@ESk`Lr@n4amW__rXEcVQOb??Pf25XA zY(hM~#+DZ%j#JWp&8`?%HviHy?1HS!RBbp=MO3!a9K?urfSz6O=3lSTo3_XJ>mv&S zJjJwo=>0EU;#hZEzJ8yYR@7%R&VL##{KV9M33(#+Ha4G*ke_p5Di%P8f2C+CIx6ep zXu}`YxZ@ZABs)t6GWj(M2$T&JNo84!uc(qn4A3FTsrn{5hDtiOlCXhke(`=mU8St> zvSYIAZBzH%1{Cw_t1aKqmxtz0J?QclbKi14a{0{P)(r&S!FDiO)$W1vy}syQC&Xr> zJ%RL89!R|+=s;eSeH$K(1~P(i81*&eeq(fndYoQuFa`ALD$ZbY8u|~_-msu)tUh_A zsuKhe12>8FdF9Rxs*+M$9i9;!1cjvjVjzxB6mdG9t~u1CHh4|}t9AOzoE=x$y0vz# zsC`G1x;|}S?iCd(vF)-9ndLimM&rgmy_{ zE~azUr!fzLRVavvLSv=ule&3G9H1hrpvo_ixTEF{+j^N!@!%Vm)*IOEPIWw zY!-Y15I07vf&t190@f7ec|C0^&`DT&7^Z)f;tq}myW`>;oDuDS+v)VF zj!CI8~~r))JklBFo}+?jE~O^850|FHgjK2? zI@%n8x=LfYv4FqU*1JkzZnL)ZYMDqdVVhwG>0y?~WWZ&+K3&9&QCx>FjyW#Czb z!L1&>pKr#N`DL3YH}iYy!r=yj9CNm{cD~EuYbWt3fpgW6L_*2y(xOceP^{VYLbw&Ty-Ab=g;1l;K(%}+5 zFptMfWzBm+tH>p(^h^`K8X0iUwVLpEw*{si7ovTK0+;~B>>>=*1n zc-0XxxBlzfpKBRy{AA=h65(kslBps{ShNdbOTQ4{3oQOHi|KKLDyt|mB-OZ)fRHkK z(7EK;rhGdL=mC$UU(8a_6!|@+DmVw)#w#c^IH^67uZTPxwa<>7NuZ*wJ`Fc+v5_8x z!U*VH*@H+-$LV)5bw%Z2-7~aU&@yF?EW9O7yYc=G5uJ)gKqUHurXIW|*CR?Ynnl1GfTE_ooG3I|-+~IN7b;~#p;3hg*aX^{RP0$DMCCCHd0jBj zlq6}dD$ID4onb%qnttVAXIJ+DvND_+&6^6V*?Z1b-Qf|;4tb8S#qA+6;;)v#vJI6X z-6A$Za4Lv$giDi1`KGTkWL}8h*tjx-kK8I+J$ENMk!e6f>|^QTGEG<>2!n$E7(1u5 z=PtJwU2qc*ij8e5UZyQ}M2Dett1`@&fUyV#?p%k2KpXEkMSd=!JscPnlF%Zq0T)uT zjvKvIwX6*Kod6dKTBT`aP;itO_>4@``&RJBQNmB7;&=`<%uP8cTnh~^nb*c}zTex1 z`)OyFbSxzJ!x|0P`%aJcxv#~>-E!(y*L5 zYj*T{9wSpft#jlrfrxSJ71N)&;3-3csEQrlabmEHd*J$OdGxExb(eo>4ded(3$6O| z)Gt1hILZHf>VH4&zyG|%KL?!cojl2efzJOJmj8dh{YM0pBa*iE_6SExRN{S>0*so>;hDi70m6i#UTwo{J4?+rU`T$<*Y0 zUDFFIf!5TvZp)JLtMw7aBmP2v~+88 zOQ4C>3kx>&*SZQQbU!pbHI)HmWIVrV`9wm4K1Ps z{aAbX{zU%}Zd++aUMp^LtS$atg=7IJEWncq=T@@Wzn^rddX{m=1F_n)Ngl2&xYcy>s}>4clcyJeSK9NTIExgqougevok-Mp0Ibb z|NZoQzK-|>zS(6!@tcTJ{{b`LFq1o$Uy@ZlzlL< zCe0N~$lBvqb4izh00Bb$E9c{qmTgM%)rL-W|7E9;m@=ag9yA8#wZ7A_6%PQGL)m7V zZdy{$*6G@CO9NIk=<;PeFA#BKE#Y{nnnV90lpbPhs(}4KM}4^|$6YLMj>KkE)!A{- zJBTF-SbOd;zF!$upnHa=Wh?{q%GRdpwrd^pcM+pd_c&QLF-u0$F}2Z|vc8CX&w zWrN>H8v{M%8z>BufQyCgQV*MSRx-d@gj#XTjTB+*e_JBueyawl&~1$5n|tpSFV#B zuq_)=zGZ*_ps>hrE9exEIOQaXCXUdF>5WB8!A@uWDvz{ua~k;tHL8!?!G7=EjG2cV zunKu}cHE|DE7pIRyz~`Pg?TaP(|y>Nx*`)^;E5(1FZg+@X$;ODo~kmE(E zG=TDklh0E}#gRagRpJ9$rK#wT+~yE$6SL=CaAN=OAvx~;tsd_4x77A2WBfOC`@aau zzpdK%2Pmbe$jPEAA-$Jwx>;(`lLVoEfKwCIyi%bfeaoZKMg~WYA=WyFSW0Ga!$d>W zmFgOq^|%|8DhdRP;YhRbioqO!>q!5p(K-(v`5L6f|ee z#{^YSoWTrXUUr4Miu~r6Nm?RBTT&+RP)+T@P|m4RDtG;0MxWIU_%@2Qy47sA4l$o< zGL{)Pnc;i{x-LDe zkf4$`h2La0fhhna`jk(fDx1#jpqg}5)OEKarS(-GW;r_3xJeRwOKF6<^2HV$^g?l7`2lp@808GhbmGWg1%-Q>(XVtKqa~L{^ zIUM0s*lK1skI~#HmLwtOzErrIiTs(d`AQ;xy%By;?k!YUZzFREyF1kQd$A^Hi|N|% zhYun_08YLln&||)J1W8skvB2Y6tqiYz6NheN2Z#`yP8F4{GM`wuEUcv42UD+;cmF& zz+&MQ9{o{E>XU3m&NSIYe|G?<`_KuXh_rjV# z8IW6~oIO!{t%jVMdbT?6aBL;=!niHL+;w>n|fJnN43vbNCubLzIh zXxa(7p_B;Ylm!Zt1u6Ajjq^B!fNVIj)1kuA}UXGVFHg;rBsfcThKn z%y9-WJA&=m!U);o3|ugOoaX)-iP1UtkpMWL4Y#8bzYaJOU?JYsagL5eQ!P10CG@@N zi=dMdLNvr!iTk~;r*Te7ia|^(_LoD!dPG4LeZjbaYAH`bRbsUF5Z{R7x!f(_olZjD zVV&L&LV5a&m`&Z19ryZ41He8(|9^eM{(k5FdBYO7?dF-#h6Fe)o6G_m8U<#U4XfAZ zYUvc)an}Yh(GAbV~{;vbMQ`<)ju?3#cP-#emf$)aaY3kt#J@Ja7<#n&UBot39--* zFG8;ZFsF3 zu2~1PR)64$DSD`5s=)ksB9``^oF;vZZx2<-^cPThKh$7Wu#WNi+SVZ08o;C79f9k$ znV(#36t!jatwY)`=*YF{75CpUTd*JbUhI=bSpNT^5h{+Zz<-sobCPxJQItMu1SA!1 zE36tF(Zdp~cD^Q>c9$W}d?E$K0v2cIo535UOVzO zO3CWk=N|`8<_6xuvqzirQBqkm#bnm*^eD2!h59&$|8DPV25(XHDp6v@JBgC5PUz03 zU@Z<=p)ro_j1Q6Vw&c`nX*(4G{~dbj+_{OIBF->Upxu4wqNh@=-ALkVB6EiVBno8y zZo;ODMez~J?tr6}VX#LOwhxW%X zZv=XmtcF`_hn&PCl+087TZoEeQcA$YmwM5kOsf`-t?@I!KI=-)A}C9A3CK@A5m|YJ z%E@Zj9Z;*;as_2ihB7!EF=9_hYF+ZZZXqbh(L(+Ytda7}2a>o9;cstBOS>KVRrmB> zZ@Qh}Odw1C)hFI*WfyHp9XRCr!OxmYE0rdY@CZFa95L6vQ!(s;ta1)&kT(3Ka&?46 z_BHi0Hq$=&1jqk2=}~b3{)vd!%rz-3}k;CPEG||mEUn5}8 z@%7%AGmNvNSSlxt)paAbT)u?4eB-Tb)_h_`ASU7maGv|559;o#t*`Fgf4T7 zNrWjQ(+nzh^)9G@-yUNyIHiPZSN4W0`kTiVpq+gZUuDRY>3}|+0Y8br9qZnEu+U+M z?y@6j=DiEZXewhin(c0DJ;-`dn5B2WLFa#FN@>${BFAuTny?>c&NIzjGDKuaKD@}M zd&h-txDVG7M%qrP3L!$1VQ@fRD+&_?L)B&P!_*Q^t2bAv_2`^gRjim{dP&dJtd4XE z;|0}5aYCA4l=K*79Fa|(x8bsL#K)pBOifm7X>3tJgE8ca587U^U&?0gC_hZUvBXR@ ziG%{&0G{xnWSz**9 zf)$3DCUseP9C0p-LDsnG*4QD*0y1riULuwcbEeRa%X)@&{R<_WFwHQ^Uc+g_FrrLX3!O49rp_Olnm2wUNbiWeU&wwHk*$B4)-tF(@!JKHV(PJVMJd&wb`mRHu+2|ml9mJXn~9|T z7;8fd{r_V4^PaHs^S=#sRVT~8Z!r3&sH6PPp6|8`Q{}=Fr*f50 zw*f++aThc zXAq~4+A)Qw=h*f#=`!2iR4=Ze*rnKOg^0iUu25jQ?}A? zs6~6{5=|QoI^Ybpt^(TlhOM4UjlBo)w`p(F{2V8lZT-8avI-~Qw;c`S2yf|-T}z8^ zZzh&S$ED$*`_VTrX`+a;Dt=u9G(_2j;;DRUo0Iv5&^a7Kvo_Q31CYE*N8h;n2P$CW z!O{C`*q!D|o;v28;FSQT1rj&UuQ}IiM)KWp2CHytHBFc6A|t(PMwrRs4fb^bAexBc4dt?G25YKtc~a#}{Nu z0+$$f@VPEM+w4i0*#KQcI){`G!g98GVShjU#1WsUvfWL9sC`xF#FF?&{53(OigOke?r^B>m#Z^j$?>V;?F+A?5{sXUI$=bg>iqVu_tZv z##gQmn(7wN6k6=jh=HBtsoJPuk>`-fNDz=l%wpZ9!w+zAo$-Pn8ZVfIONMxx~d2W@pU`haHD2NOXosFM8t0n zzrY_tsGyM){7)wa*YQuqQ(QHI(t$JKT;@F)&qu!|(%xV19(V%$;6fF{W#)7d@@c{9 zTnZzkP!UOD>}-qi&nSbTZNWOX&@H_{PT+TFgm4?3Qh8*r#f|5|w$u3-p>~^7P!Utp zjzocD>EkRYQtPD&IRKc{`|Rd8r)!~QIjq6582B2D4%O(TrH;ZiT1a8Es@%jFa*Q}) zBFrXWO|*Z*-_~ea<)hnU8Fo3yWQVM-T?Qm=T9iwecNkKhULkoHxwUa{m14T*hS)#3 zo21sR*}|fk7{YgfCSUK*+r!Se?Ih=;nz-$Z1c&J@?MP9+{66Q}nt>`Brom#zYd|Dt z!wb>K!H;Jk-M}VKZ)6>3(Qb@38ao=5P@(=VPCpKlC^$2*CR3irm z7|Cja78rK-Ml2;R%Eu!-9KkBjD}@TmdefT*^R zoKyd}%C)zRL3XaKXJT}qL}aLp%i-)d53vW5&4v&~10wkiuKzNGqoAnUA@iACNqlM; z|7j2Xe(b&l{Irkjf|Ln#!)@ARuEQ!8K)ij-o>)>dYyQx6%lC1ja}q6h℞a^N*Rc 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 tCZ{IFXE}g&_cu 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

y|* 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

    @9 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

    HT7k68u_6dfT)N`b6#vMTF1fLCCC-&U ze}!v@yx;Ui;Ojl+rcM(r>A#o?XpKVpo2yztCth?Dsyt5ll114cv*60m`Cx_q+`MXy zTuz?o!Bf;v=~J3D=jsh>s7qX9>}2*NOTeG__Phf&nL++fNbI6zA1AOvtD0>))`zXR z7>|%f>ILh-U|9&1u@()S$6La-$P9<>`oe3Fec=$=s;sadQ-#|=7~GdCW@mf^G6-u} zV@|a@!~BMgaRy-+U!u2=)?=cmF4Dkh_?5lq)D`Mu(PwzSzSOz)3vl^$Xaiqpzl9T3B03ZSP z!q~DtnId*odYG9jM+NOU%lkHKKX{b>&f}>wOmsfnKqc@gA0--_?B_3!TMX*MD#j&9 zq+iX%(flU|Z^kosh>BJD((8mxX=c`miL!FD8AXAde9c`g}o4zh$A`oh}RP`Qzsc{q`AobjRUJ=^vH%gumz#XL8*?`@7(x z3#`0VV}M^|PNXO9$1A}t4mSh$D~f#Z z)m&UYLn#>DuD$Fxs0EeCCVOAn>JuQPfBy?5U`s|*2mqb2(UB0z`&k%^3cF$dI-*t< zG&f#H{#~zb==sYoB|w{mR4dWvD67H|7N1JQ)_yjM;+~GQVm00LB#A`_Vrwx^31g!p zM!O;SxVcBT#gTnO^gbmTDqCNwO!PLJ1tz4;r*FgVS_Og!2n+V0nFjtjKaNrdxrvSG zMW!F4PL$dtvSUoB{z-RYRmN37ZItGg%oLn&l44cK%sLwoaNc-XM9C4t%!+V1z|xe+n0O3GcDJ5O@a}%9h<=Y8@3T< zB*Lt`v5Kzck*R)TY50uF{t(XCo}x72M4YRq;rNjhMKydbHc!_M8{KN$sR}MNC4S9hqLRb*}dLw0_>=wNdbpj6SmsYVKS5+HWt&#=RD6c2*qYi>P1FKYOCuP(i$6;@`vd4_?#9M#(ACJsgdML&FO#5+4pc;bWDr^l3>5+C7>{9YF*D(<;l29a6|JG zH9jH1GPkGeWK6Zf@_dHiM1xq!+nS_u`eMe^e0oNbb~uDy2okQhge~OnT;~kIbI1(0 zkgQntL0@x%SIbEY^&|)`y~h^pgCRFX&DOj#N;hQ_n{0n;17wg zpPflhyG9twY;GwFg)@{XvIwEIIXx=bQ$T8T4zXjWXr8QTR~c>I9wua zF%GEkEY3Y*~1i1u{90#fPqoG~{PJ2AUb4zrU6i zhMy}MAg5(5Z2iZY@C2-j`~v=+MAn&~m|CBA3QMD7(hc7+K>HGUe|s^|=7h#Vvt9Yd z4fGf9>)M@y9w0l+!2^HU_Z6xZq?imbS40(an9!~@&h=_PRqkr>GSEVNn9+ne6Mh#h9)d}1>@YDpc*vBx6A49KwB z4&TmVR8BAik8-5|(@v$$bcG3MsKC}xCPtzsR*jfE1y?qmz3q{ZY&WysuRbS3FcKy- zA{%G&bx{=370h;L(ci#pcyYv|seaUw*XW1calS0#KkyPyIWOAf;;qTYr(oj^vS7;! zsqS5<4K55cgU3)u+gLC(Nw%Eb>0SKWCjpX8hMWg9iDHE%#0Iy);UA)d*Vz z<4k7)eLV3Jz8i|GLstS8^<2r5gkkDrp~X=J&S<~Hy&R=3%)319T%{|2&?s5;_v{{@ zwr^k&$@&(W2dw>NE3qz_-nDqw)>SgE;~)n(vnlbC53M@Pmq-gc=}_QWn2@1=`RNa} zI3TIm!%g7*&gnL(!e9d^K~NMQzvg>gJjFt9d%fEWI|6h+Eoc@%H{x0|9b6KQuF zDQ#VJ6(Vr=3O*^!hh6bja4m;qHQG6WA-+47`yoFn!J5-!>#1+UVLV2{7$ zkP`jhiaR`Nm(sOBX|>Z;;ig{hA*(#-_kavd2ZW~c($=pN2b{&xSp(Mc>D?7>>h&Oj zf{Q>!yE_4-xhe)>~eDr!?;b$|s%~y8@Hmu-8WKk1mkmzj@Dv zQaCb=CbMU9>Em9J%#qqziN5ZC?Fs9&LcL{Y^m`^;jX35*go|q=F{qoBYQ80u6zHAW zYHvs}h-6U@6_m9%_>-*!*_&K=xrWr<&X%7)=~yfCjAWwIuQr8kek?yFjAFiEjM9QT zXC}0ZoO^h{ZiHIH?U7g|o^S4Oo1Hv(6z@NB(<8HnX+hV;sK6vE?8s@&<^Ru`dY|Gq zYYzA0#{}O0B?$Sy2dn?bc=#VxwMY}%Q}x?)=)lb4!0crt^@lMM2TF!{6bZ~M0vZb1 zkW>_GAP_h4ARbQMd@fyzv?&ukbt&a)aYw(&s=`i;?5CApd9t;Qj?TLFvU<^5cNtXS zSKIv^vj`FC)A(?<%XY`}#{HK?+wtv0d>G+RMg?qs+CBu(+$UmK9_L!5-1yxFeqV&# z)LmG9`5wx-)bEj0Xr}2&ictw1$MGJY%k~E`KVYx?a(EhB z3xmJ7-&<$?ibXW@6fNY(CV1Rs`Ucv1_<4dTL_Y8l=Jfscdh^51VZVtEAf$}8t24G! zXWnWeSxAZ=>_7{Al&fowoN}~i_A5xZ6sN;8vK=C9m!TFsUbt#Uik~ps(Fu!KZ-8&T9B;N029e`7!<@x3OHadvvlMYOyaTj_m6&&JcT=WH z%En3+0k?ra2kmMn!?V**T{GYtXr5EDRK2m{u{Oq6sxLZ28)}gdmP^5>M3G{3zlFLR zoaGwGfd}0ClYg)>2_Q#Nux$*?SvJ_sb)KsWY4Bacl~;6 zGc;MT3#w)@LYBUwa+O}k$6>5Hw(?rla|L{NllO2U*WdUy8hO{s7+qHQbdAMM@UBMS zDQqh-WJ=aZxhqATy>z4V|1tw-rbX+1caVFLR9G9=dAe}oNRMIblZVBO{qjP&Tv%R? zgL_};Rqzy2RPm9R!>Kfwk41hKah_V7dW2yKWgA$cQp=|bVW}g>NO7gaWeQZTFIY>L zJ2%F6g3#3LDJ+8UM1+B^S z6d`xm`_tF=tlbWN?x3Xu4!o9qCzwwE9u!#aDUf_%A7|Fq4vJ#w$i0L6<1Z?utMv_R z-!T8OhdtMHQM!xNjHkDWC~#~%DJ>F1cDCEOCjSiZ&v?Q7tWxCOgJZoV#ACeuJrH)> zV(b@PhiP-F-CZ?mo$3K2hOg9S7nx93ph$vjAAupIH9McwM*bPq65P}x*BKNP?T>oB z_cx;7Gb-J159*&BLLQ|3+c-{5%dSfGx?gwwa7g90OfLRe>C?QsF*9 zcWYNvkJU_n?BbsyU=Rm~5*t`lu$;rjdu~OemEIpw4&Ao#6M zo2+xux?3l8?-_eVQH&3%d8RK(TIs1=qfdDK_X=RDf>Y@>+B#DvfC)9;&1(@?j6Cb3 zlhx&gHlJ8oS!l00Y`(R#NG1}4dwbbN?B?w~$|(e8!OjMb^4A@^EQ&d^AV1u<5Uz%# zY8X2)pevvgt`VdYFZ3D&k8W>GmJgb^?x7$)(X$hm49zNHuzysY-k{A@u=uIdv0Tka z(PnNbuGI*;PAQO@0=wZl8kK;F6klS&+lU&&82JzFXWh>^ln&z0qkvpO&AwaPDSAQn z?H{P1eu@EpBWo*R7n_dYWA6Q3G7dUpOoa5Yuv|G2b*j7p(y6zjg$w2jc^ESZctg0! z_+v0{;fGJiUIF#`o-flUAs&iN3O$tpQs_a{_jt)(nh`Fx$!^VKK33sjJ+NcXL==ik zkOo0^-bFM>aJ*ot+lyfv(jL9n1Zn93KdBsUZd8mMb>-aPSr2&84B;ak`N!%o?(6h% z8#&#QOlCl`qSRPL3%#YaHn0n0t@mhqU9HQDyGhWL4cInx+d^?vLYFPWezdKn@kf_2 zbbL{gZ-wF6%9@ZY^K*tZG^vhUJ6h@3g*dFr+BVCyeknKN~;uiFi4q969RZs2CG!O8QiG)XM4Ed$@o$)><&MO3Nm#( zCThvEt=0r0Iqn%>7IX#B8Ge2gFdv8@o-M)B5ivVx%wAFQwWx-~ zK`2V)-jMTP4QHYFI*QYO*f`M(M*$v)5SrJlVP(~j2trEhei~69wP?(3F=A7b0FIkzo zsRx1MG>HH;jfmweDhlw_H68{CX8c$t_Jlq~l0Jz-xuf0(`J(?qBE5FNV0QlNglH%8oE3xV1Pf!b3y6IJe&} znZHMY{pcT`LY;bD_hfuoJm2=iF^s`|yV8!wcmd1P&V;?E?)_v0xe8+w@aFi8EmWXn zZhqcBahoNr>6n@JV9jaYPG9cY@rmByL!=W1fyE_`5$T_j+(DDHQTA9LL~;!Kp*T-s zwTM@p-m7ERSU zkA&;bz_*-t=X`S1UyoO&TjRi}GRZ5=Gl@+0(z2ocWRuY$%)3&-LdHCyz5+xIm{XA- z#!eo#26U?hCC^E)ltA6XUtPAu}j5fi%D8#yxkciy?m z_aDdq_H$Qh=s4nPV1KG>TU|b`)!|b#qipnFkWs>@W){nz%_N2<%ixQBi_OddW%S?Pd;s%o!te1P7Bx%h+mhtJ=S`|IEL z5!)4;^9qq_9%Y;%T(eo~w@SVfJrxB_2QgJWK7u2!t$_-ZoWI&VqK*;^UXvotH&iCJ zx{P0}z;l(5do9H7xn_O!ftbN34Ad_Pk#emPaNW9rH9HkBSE$!-=y+&8JxALrsr{}; zxyxAs=p6g!Fx|vPdajn&BX#i~Z{8%cBR4a3-CcZu{x>qoE);2>xw7c0k_#q2E)z*b zX7vmAUwmXn=Yq6+=s9c_lb+<+S#@&sa0+Ybj)A%T#xX`ZEsUE#m4*D6 za;Niv(?X-uGAwX*i2KBONk(S{?kPGOh>0X7Ru(pUO~60T1eRpb)3hACc{-J7m&CPT zud(psh1m?Ss!`pV4Hh|(3=MEbV{9gt{x+dA`MIpQ(yC#31m##oz|K$TCN5aOk<~ss z-#)dT>OPxt0|$w%f%TGPaS6QjO!0(j4VPJAu(r@^0d&8YbFu&+XMlaSFTf1@~9bryJiz?2%Kzpe4i;LKZ zR6*Jsk>e;zN1ih@WgsCmXU)OXJb@kbp%&~*p2cjf14c56NxDHH4f|>`a7sYd`+n#= zcqd$rjKk4Vf!l6yGRZ!QlivLY6C`s;2St0U8;PA9*Oe6vhM=R=QtGX(;4592F@|P} zqPjLT+Yr_IoXJzEJsF%2G+X>uI>S9UBXPPf35xH$`$DZ4c;D zjU2RERMWvflsUW!o$L{tL1q=`VWUA>a;0BPhJ`8Aw?sj<*5;A(w9M=RWEZF;zn#Yy zEWL*!vHglm3)E;(iqwdfYhf>O2nIwd{c#9}060^7Oz_*leVnOXXmADVmujr6;eD8# zxf?!TgMRl$nbPqvedx_E6@HtQxYAHWW7&S9*zjFMR$Gxw2>g9;83W{4;f;Bm9AHDi zb5`&_wYa~o&h+u}r&92kj!MFV3HK6o{f0j*JqZ|QK5%}A%kWa&-8Y6}GSwKtB;sH= zY6-Xf;w&pc+1KkKfiibCmPa+tBSb0+oTa9kuz3~-s}{mzF{jUZ5H zm83S)lr@uk-kLx)eX zKj%@GgW<`HNe*h`Eq%=xKaOez_X%^;(->);8V~(%lBz1F@(qdImMez$JGUVq)yaoq z#R-}tv$t%t{$<_V$9rhq(Yv^6Z=gLJDJ6Aw~5<( z-q60YIs5m6U)}DhH**LX`SAGCT2mL4_EDZ`iiltVK7&qnmdUNl_LrZX#4*-xenUpZ zRdC}cQ8yRB@=&@^97hp%#i-W=+PizS8P0Rn;zp?8LrnsL9=g0Ay6=feI0CmEy(N$eX831>R^B_y^uORgtJFwu# zO6cf`#7w7%BhG(fJUUni|1z|Db8rT0+OklF$#uvBa(d9#orZw*C5Zfgct)i7Re>9 z5xZHRV_7|74qzS$R_J1}>gf%5yKgO%fu_XNFx`$TJyTQNr&tNhor@AM+4#(QeSn2@?P(}p z2j9H>m$&{X^5Mp3!_v-Sms z5f~vqiQ%p3BUaN6`WNT*C|=^O1ozB&`HTD-HhS+rv_{P@gz6oB6iRJk$8^r@M`F{~J_p5^K?0cn!n+~<_#yVc zKAQX&Z{q(A=l+kwNs+qV_u=Fl?B?D?Qy6|?)(}yGi`kELpXt{rk8T$a|JTv!l%E@6QP6mU^U$tFya63fv{SPz?AVdqo_Ya9Uo4`<)hW@)%&nW(gF+qSCGwr$(CZQHhO+s;bc_T-s4J=1-j zx#-hZ`v-hG;*D6bU`!;ds&WstP>@uwLGvP{@&|@ewaNU}R#xG}+qh6QZG6hTLAy+u zV&<3jP|YD}fVy@6;oI*bc^Wrw0^4VDbQTwXGHW;8!TYUFE{4N5Kp98W@x$>`?oSzy zuR&81owPF4Eru3MSD}>cC5E6>6)bWUbL+5D+&fRY6P3|o>uciD`JYq96$6MvuDrf3 zO})3R@mZh-4oxPxgVDZdkt%r%N_qC=d4#d;e8IKX_k@s9y5tzMN}5x}VZWv{cxPW( z^hgyWZwB*tJO6?&igRK5k*3Isl0$%3%a6SrPD0k&5sgcNn}IrVEiE2$_|Gkbkbp;- z94%;$MO7mp+_J8w35{<2EmWmIv_T3pc8gj6^~%g@0KuX{klX8=d(*Jw^+nWLF=g_S z@iR@DX8J;z=^(39Zt4S6Zp!DQ~)ydkp>k(O*9`pZhLB1K-AV;i8fn5atH zRGcCw9?p2wO|L+{;tqEBqJGugq?z=vfT^gg@}yJ|#1UR|l;y+%PdTT{3^}%F`#d_8 zd7*~X%2^CqI8lSl=t3vwT~}oxOVl|@)zm1pCD%gc#=B*fmfGHNo5@v?rs?10sEPM zp31clT&_sh$CbQ0aW~QW-KW!pja`fCl^9*0XpN3GgW2Bcq(A#N z#8s?V_=Yl|Wr4+CfT;)j#)uqEuajQjRsR4-m(Xcgq{##}-*YqH^Eqa69(BN$h+_fU z&rAth%_ttHiIQ_<>#k_0{ZqJD)&4i&Q%hqAn)a<&Ey43X zoE6NQ4RCB*GY=pvJH*RWK{9DGpVL`IqQoj!QKW;v(w<64^J6>O)fg9Uz`^*7?(2zNQyUPrHP3R<5JUC&sZMPt~u!ID!9wuaNR-mL-Oti1}yg?A9iwN6+2>84VW4 zFvCw8PE`n-s8W|8;d_KgPgay4T#IKeYOQD|f2)M|2p8W9ez>44y~j7d1Qgtn{n18U zdOy$+mf9Hvx$5J^J_@qOJe^+|C#X9%b6DIP*4pLi2uKHng&o!k*D4akiku$Q#LnME zjpLKuG)t()9Wf$a{H0eyDo)qMzI!)_KSV0mTtweHke9hFUPn3|w+%Q^OK*hw(?<|K z8sqAzsa|}g*Jv$`z2N5f(H2_b>ph9iJc*26-52dh4`hjqXhPNT3Zx()(qe(T-OkOo zB@FD#G&ch~o_V+om9?D?Nd<^YQ6(I%C}4#H3m1(U`Xje znbb+tR_l8hYG>QJod-B~XsP_R=-8T~owL4{QTLj=M-B5J!qDm>c!R)si3Rr&;^)mr z^qIQx7AX3T&il%K9>%+&{t7+5A#;6Uve>2jV1A`~{6^Rw+`&|IC;kLCGHkHdp8ute zXcy2Mk#1Tf5_3Av_No?t3=v?WJwh%c$dXi5%ln7l;`_F>U1L;{nyAfs_zU>IKDHxI zi*hQJm7EI{nver*5o#EKb98yOiI8U9l;`X4I8Dn$z$BoSmDgKKvK1N_c;&=4TM zBRw$GNv=FV1fX9eb)f{{rbX7vNDXCO7MImh{d|6Wy(+Qm{_0Ji+jv3l#+KND(5>=@ zZb#c`9y4tKsHv$1&viX95*Tz6T)T1WM@-PA@x%HnvG!Y`G_BaD%S6`H^HaH@27K$tVSi)R!& zADuA7cZ{k#3aZ|wVjdQiW}f%d{N|@Y+r~kMWx7O{+R1BcDECn?SS)VAko?BNzC!x$ z@|8-ClvXQ80uwWUh$670C+aMsb(&Pty)TvyKZ>caAOmsBWEbp`yGl15wJ;cxdEyr0 zld3T$`8~hOD9Z=*MT!27orWTD^4CH~tzz5|b&WgnOCDu78nEetb7}Y_m_tEYf<9 zt%{)Vl0cayamav{eS!z@>E6hMioEt~5GTt-eMBEvWRuj!SOjy~f}BCM{XE^p%!Mb7+b@@)> z{$}d|ax6rXEeD(JSMlvW2QhpEQ2!!~l|@$e!p4N@M%xKV$R%f}~O-ub4`u z>^b#^>pO7IxojO-=Ec9EocNSYz_A#p{TxWAUa^vgEl5O6d<9)^)K6Td*xo{s7*k`g zG9At*r9Rr$(;aIy#bwm#Wj59!;IV?uSQw5=L4O^jZFqnL?qvB&bC&{*klbmIX00}e z_K+9x5+HjEd^UgZ&R?3rMh&r3LT@UR`CUUfe2Waie1h`>e}dCY?qgj2ZJV|Q0e+}6 z$vNSzRwz z3i8FUkv8q!UATXRr}$s>Up99gEiZOiF55?G!y+ljgRwEWz5CpJjv#u}_m!g!SaRs9 zr-SAGh~o%~zxai=sG+bs)2A{H9k)ucKsKnCum>tvPf)gJOrxIW+W*D8_b*Sk~$|*}qakS9Zpq!iDu1s4Ihe|iPS>J$0hJ(VsiNGPpMF7uG zO5vBBix#WK?b-2a5TCzeyo5+F9$MgVpR}eBsx6nZbDUVd_f+g%lRNZ;T9K;|buZGT zraMojRmh*vrjlu{92kiV-_3+i-BKe$Be$J~88D7w0`hXKP0Ytiy`BD0VSq#jeEUWa?jB>) z^l(baIiJgorLj=ey!haN8GA)CnC zPf$w8&YLl}c8BTRd}gkfoSI)>Av$(gzn>)Es*m5I-Puojs5=_@^P2Ab8;iTM=+aOS zyXhD*w{WSejRu~~6WWiHI)KtO8gR10xlNbJD;b`f^@?XS9{!ATBv&UvN7U^ z^-i87oP&Q++7O?V&4PDI?o*)`tIhMs7(m80Af!WZU=0y)PVptqv;VBFf>yM6o@0ps@Wz@x5|@t}nT71adr-Y@M&RW`5U1 zB2Yv;^Hsmx3#`O^pKvWGMJ5G5gssEli3|d4&$)#7bY$VAC80Z(c*25IsKua@mVN0J zPK#Hsl?PFJCdBcJjX=%>?5*D_3guh#^FXKNNXzyT(_anVGC2dci+UXeQ!O%b}=f?@tn?J9cUCK9VH_2hJN+Y*=ci^i50(82u&tY$5jIu zh56fdmBq3l+Seq$6L3kqLb=C{V(N1&OkuLWn67`~9>4=!Y=i|CkpEO|#ud(Opsi3bSWen8%+%nZDP;LNvM-offe zV4d_tX+z*lR_-%!K;1^($8%eDx_{UNuG|V*6ECtateUGg<}l*EAa>lKl)bUs`UrfU zXpDbe-+*@8h^<%-j8~Qn!V|QL|2nzgnAwiV{^mJ&?}b}^#Cp0c+J0Q5S^G{m{}!F( z%fsSpA)j0LdqRM!tgpU}xW$|mCp`17rI~d#8 zTm8prrby{$7*m1gF`(NXTqA}@#D%4SINV)jKSCgf*EXA_N#I9PXG#t< z1#da`i7XOW_wEp7+qC|WzAcaZc3y`DD)*>|t)zl{UNRs{=jMl1A;!NQVLq-?T}Kzv zCNiq^2t?N{U5adiID(;puez{{sBoHd55&^b-CpXrrS+i3M32U*>C?BAeX$`<<^7jo z2@6jEe1)BDO?jYrzPm#%>7l&8VOmUNB2QZ>9SL3L&wcg{cI3|6btZj6P6R~3;^My3 zkW$V^;U|r@VkH~t$b9f2m1$@)N7h=cdHO2Y@GfNAQ9;HzU__umxmu}HAB*Bz6)QXe9l>5A2fQ`j0FP_f{s}>lT?N%Rt zF!sd|FBtHu&3R@(Im3k`iHd>=ZH@q{T7bYBA*A|9LRb9MK|9;`V@H<{u|Iq|lAW%Rn<;r4FaD z-;nEg!E%sghBAf4vhs$Ba7KYBY8i&`Mfr4p%oGthNi2bZA#s23=dfhx`)97}CrCzJ zlV=VnvXZ6f2$MWrB&{zRC#}?pT+uV=30dJ=yMuGwryG9BFz6QcYx)U#`(%hD@{RkI z5=lI&w*M`JRWs%hrt|`!Z8p0132P3>q5W-^XWz?9ctOTnQ2B=z<0uH$J}*G?1RqUu z8TGq+aE>*k#0s|H5N%Z;l}<^#r+;1eDtySlE2w&48OqV*rcj7CU3SB7T7O6P13XS6CH^@~h5&-KsM9IBUgz<}pN= ze{-vH*0VG-)N?eV6aFC!x&3bfzNC$T-j4y#KW*E7<}xIepSg@t4?ohsQ8Qn8Q5_}> zzG+$cM*f$gc`JE1TP5rmI%r55dzxS#nnzyCNOu#Q*KH9UZ=;1+sZ&)o?=zZHlUDN% z27l-N?4iK)g@gUm_RjV@*Lx-(?#COJFWapY@+Es7A&w9@*;y@w0A+AND7BRE0X=0% zf*fo~Av0;wg8erJ%D!CXP#F32US{I7h#er>p(DR|O1>*z%Au`L5Uvmv>3Q6r=IAFF z3auo0>GlkHz4BC4cn-hI(@3TS$}|!t4cp3m6HaFWmP-_Qj)3zKcJyqn4I=mq$8cw5 z7nAoX|B?-i{YC?fYGl(=o!n(ctNq}8ZKM;;xg*)RyGdrY7`JM(W7Brp>!Pg4D~{^l zXKwB7Ne-F|8R=qCoW)(0zb|e3bg^-$-X69v+As#5O644S8>Oyee-q+TzkNm90T#`S zF)o9eS+ue*YlcCPn8nHYGSzL?6;~d6jxYN(`X}~!RH@J_(KT&9WxolxokOI=VXC#)?3emX(K6fPQoK3f%+;ND;O#| zT`!J_+v4a!V;n(D zyRr!IJQ*#PMihyay*nel8!)bqy}L$i7I&CCugz@+Q`mx4m){;5GW zv|)UvEpeh#_I+(;S9OI$728+n67NWTs!jT-n$&(MqUn+tWw*VJClJa~22*w4BHtV{ z$n+4ffvOdkHiL}}pqZg(CmPkL)SAi|o&3)|apD%bVQm;JV2C!L_^ctc;~3=B!+1sk z@+gy_WSwD<=@4`+W6^Jjih5zURW_ zE2V1TyPoW>!q*0cnB(fe5?8MpKr2irbkHksnvSp&AV@z@e+p<(&>pZU>;>oyG3fJV>q4bgX*F~`#b~ZH%&BQxZIA?M7;Tu64lNR3GanQ zM`Cn^uEyW|g5U^d)g8|Q!(u?z-UK7+qYCTl14L>|ygpJzUmT<6OEIctXr)!O1W7eO z!gPItb3% zu=CM~d1tsWku`blj!Qj+Yr^zgx&NJTDlXL?j=}AEN7zQvEZR219qs&DPXOJ%T2oDZ z?W9ID?Y6VIr?@KEe#?b#baGl`Fy@ov%q*!O=21^d7K zTE^Je!RRMe=tnUnVq|S(|DRg6l7<745xO_)+Qm_ApuD`~_#fFo;vyI@A$eGTm=Jw+ zSejW>x#ls}E!L^vsp7`Jk(}=PQ7W6ZL`?2|L2Qh`sD{C}^S3JOU(m+Ca;_!PmBX9R zjN@H)uD70VI!ow!dR`7Oe1Tu6BiTXuU4aAqGm#EDqoMSE(Zdf5K?~7GN+4(sqx=ze zj0`SgLKXg{0`hDJ&Y+2OkU-Bn^lFAU;cyH`H5)Fyoql7=Qv92a6YX-ISY{oeL*HWrxiH@1KC%6$ z@j8S@_VC)MRQA11wjRP}>>G)%-%IyV>#%BRWM=R$Wi(nqPJd+hl$f^_vm0lsP0(cA zr>QkqqDjq+XJ(|9s%^WCRJ)c`h08i<9?eVd#%eX14@fIa()%5(rUIZ*ve2MA7%--c z%cM0J2j7mTb!y$4RwsvP^qoOOYzA|$R41C|twQQgU_W}$P|JzvPtG-|QG`|>gys=& z)6q9p{ep)Fcro9KH9;)~n=G$PjE2CmJms{Qc9`O)3L~-rP4{3)EDf2j>D57rmRC`6 zT0CaAo~y1+4RJD5hixJ zqL_wYoKa7#f?R87UvX15x!4okXP{)8Fnk~=xk9Z}XinW3JTI;l(PgcLNdRRvi14fO zC!QT^rzvvicbamGOMumM<$gdf zEf5*0^vg(nhm(;=LL5DY+3dFhDf!CjIw+hNoG>m*@^1x`+h-D>FsM&Gxny-Yh7a%d$ zE;1Lw)sJ|am`7^?wxdzXI%$u$*x|pTxFtPx&K4$rzQvHB`z=-x^A~9_ z=gR%pqhI6tpY|~2Ub)gy!a!|7J7}z1!0q_!LZ0nm=~pt;1iLhZ?Y?T`M5x8vdkirf zRD@>w{`mV+&!ri76NF)r`O~Lh_X#sW(mYY=!d;>stg+(v9n%UZUl{~+Enn7DZ}3Nk z2W^%--aoQ`Z153W{bT(*PLMTK`)WcT%ufTGJ3Ih~KT~|1zlkeI0jv_HgAoQvEdTPo zNNe_KE1$s&x5cWE^cu{PLlFisw@I%%>B%&RN94xjWAOx{AMl%$UY4}*tcpkvd3Wys z>_9o5vE%n#^P14|*1b2i6v*N^!1S^4x zsUyIW4l`IC!=woRg{0L;H8^`^xPfYgHR8;=;)1r3L&%D06ComT`_w9@`dh??8bNe0 zTQ>ntrF37XSOK21NX0Q(oCFlJvw*0zufB~sX(aJn@`+U1AezV)YQ;7KGbipHTW1ey zli(9pXUyJP;+@junH&mhKZ9OHz?S+?MeVa~CZmu&+2|`VnfOIYCb^w2_|;#5apt`j zP_F}jL8ch&HhQOWcCl&vdPM2C2*e$5A0AVpaqmom721sknC_Hc7AGewcF8(?C6hZZ zzNN_4>8rgTJO6bO&*hpg_WHSP`2Fx4rT=H0{r^~D${RU2Svo5JpZWa%j^iq?E=VgV zU&0Y1$zZmfwS&(2g%qQAg!r+|?8WFtjVBE_{`xlB=3-iC^JK@R9+KVPQK8t)rJl=kQ$K6ElXEWlqVA(9}zb8{2&s#3nE-5ZM_c~wq z%N+n$!#Pd|{IjtseK2{xr|`JLLV9f$%bCu9Sho+%$uBYOmqYI!m`r@XLSCS-8^kz91eYd9sHb{-NDEcJfpR>>+{F zj$Mmj^yCUmxxq>EYJuGucy`JJ*8?^-x0CNK;eP?tPF{nvxGhlsXhkgvawiF7a#b3U zVP3q%inw0i_*s1<0$5A+hI|voI*f;L*cLz}1a!ykqs>=s@v|MHhl?-GxRPAvK~{=Y zFKRPl#@sDa0}hXj-OK1Ni9;?V$H&dk-+HyAm&eoihcnHnQdmv&7mU}X)2dOhD6zR% z8%FAwtT)Bds}l<5*Bd1%s{34Y1qUd(3J&&-oDYIuK(WbE>X{T}jLu23_(_e9j6tn# znawU8<^>1Bf@3WXwMep7X5slPr85{PB}VoV54E?ZVz;i=JEKuBJd)aP<7w4l6jYjj$isZ>GzM%et2mVoJBz{?taE)Hmyws&E3&O+ z!sUUC(}C?FmdYCVjD)94FBX~gn{kIIxKWOZI{O4nN$Vr>+xzkkB1zE@=vLwa^yjb| zjjlT(V|RT4ZWBr)DV&0mhwO>mh9ohrA|okA9A>N;QvECmBF-6VHL4wiY`kC+nu)SY zc#QW+FN=b(k)_~oYt*G1TxA|zcU57L*s>Ea%S38ar|M;Kkrs!R6vx5(Q89*;)I=sm zS(zH^RTEdAIQyOOjt^(-6Vehjh|&Zpjlq_HJMN!A*KxT0s@s*o2D)^Jpl`Rn(K(px zAvve6Q@t~{P-+}Dx>)Won-RRg8+Aeg3PmB0$M#2&%#6S}gRsB_=nJ+gEJb>MaXlpm zdZkW|{O?jgd81{}Q+vIEa|VDrDXgW7!tBdEacoXnb^j7>Gv82YGSWwIJoTNKOW;dk zrjGdg5zB+Z>WhyU0o&-!_3B~I-8TOuUR4LPP_~*eF`-Dba&R#4n-3Vz)1;b>e*<1O96^5|_~jisz(xfq3 z<287P-<*m9@9f0@OTnQT@6o71QRhkG(P-l5N5uQQtpty&OwTtl3jZ5eDq@y2KWpp$ z#tNzus&T5%N81Wh(Z&UDgje)=YiKQb4;5NZwd*p&7SAL-J?gJF$yT6D-0?9kj@^Z@ zuli~RPXm`|L?VLL@7#nQkY79G8x?ZxpovvWLL3*jmKZOM^+jVzyJqiPT97v$+?+A# zrc)bvYA145s7-Umi&Oc$?cfIbxKzeK@C~81mCM6S9xN#NJ=6B2WQm3P{ugDH@jDDz7Xm z4(jC8(hN!CO@+gY5|x$*gm8?`ttcZfSG*corsar^Lbx*+P`vO(1)9*3NoZ=2F@*qS z@?@?C8UJD$QFn&2AZ6XFc4Vk4JF^&M4c?4mtJ4)sg@94%X_g(}8PntSU8br5v1@{2 z4+El9bArAwy->Tn9Y8)=W*0TGcQ5U%kS@~}XzlNWV;OEcW3;^9;9BEFHP^okl)#5O zH`sq1$GkT>sC=Wwp1h*OZm5p+7N`b|%tnu-?U1F8kj+VLZ%s;5#9U539J{H1{HlY{ zxtShVW?Ze(c9e3e|4{k6kpFF5`|GX{>=pVkG-6<|^qqtGoy}!xH<}?2m>-O?kvN~D z)zAyjC0mf*mUo@S(qrPu03J6rcy#Kwlm}mo$DQ~BM|01 z62j4PvVAs!H<0R$IO{0r=9+zcxCbCdZMP~{vF12lNatvEeqL1bs|+FJUFyG&vD+~_ zf*=!1apf<`WPU|fU)%y$?e{u4(HWofcCVOGhyfdG@5;x?_ z`1fFSS9Agban7g1YLD~cVBkzEU6s*)3KctsS8|T){3~lR?U5$36bJwS@Mn#c{~rxH z|CFZdS^k4U>0)DVA>`_4|6~6BA4h~L1qqoSyZA46JZNg*O8juhZr4^Ws7N}+!Prs+ z0Y-d;fH{o@u4H< zDW2}P^0)hE7d!y$0b9tZ6NcFgerO&T(Y%@7JmmOMWy)G$OOeDyaDgq1T_~1u_!&bJbj0HT|B2Z>*-1k;z2zj8mr9XjWwKER zn_!8&$I(<-HlZ-R`7tmkXCXzwJY?5Io*Rj$Ov<&se5y9(%iKNj7?n%NXF)%zm2AjP zbB3F?W5E;JP38FGBr29tto{Nl0oMDPMWK`nG_h83Wf5ja(i1E)*@(pSJeP^lQtl;v zq2TWIa(ZSds_dMczYzq0gFFHTb4t^o1664kJJ=<~0ris|2K)}q29=$Sidm3>HOxhn zu>qXDf>86&Rz3bR;$sz@0b!)fOpYtFF<}Gj`hQEsChRu{;iXgA!xW&uo%kC?2_$3Q zh2G~cGBZ@5m1&Tpg&eF1=E<|Dgz)L0a)fme>@n5-1@g=rC-=X8;;pY6smsl3E>1EB@lN_ORhL;w1wMJ55fN;_ zTVzl~o6K-#sQIqzW;LRyxkQ@CivlV7Qiuql+|RW9t2d%cap+eEiV&f7uy#E7aN~OS zSqBkE{FY$QfMPkhorl?;J*G+RDXgL^VyS31>XkMd{EQK@qDdOouZ0`7u;%lN=;8+a}Zd zwk*j^0HB!kznPEt|M{E!^Teb6;|8+g^c7<)Y{Tr% zFY5an|92m?zVYu1R>a|p3V23+h**CxGip#crtwMefc-+Jvtc{L7VCyaT{3Izjd6ZU z+sIXHeV0DF`9i78YUxc!u`FgQ9I{iy@T#xlZnlWPU%-zeB_2B+*Vzs;uQTo>-M3F| zPyk00%>dM$$i2{>S|KSDw=kX!*gnBFr$1wy4{oo{UoK8h6}C>q-shVXzz>u!b&79N z-<}~C>JKe|Bv+-d9=u-0kKULSpcju1S)H%G7$0a|B5fL?KEBhN8O{&U5TCi-A8($@ z0U5^^6{8P@q7OE&PZypGzK2to&l@J511TuJl~**t55(`XiJDT|Z=>BFVDNn^Ksm(uH??-%W2ZX9*lK{gj=ro3Dy_k_Fgn0rld}W zQ4L*cvg;UDVMR>-aWr6rA%szdp=@W6Js^8y9R;V8XVcZCx>vT59s6;D#7jxxj@LGt zC=x_)B!n0|BQ1Ow zD(If4pL4F7wWjRy`Jw=?>#JFjc&Fl|`p<$`N~+H<^Q!JRY7mAgx!lRt5+q#tfvpO% z2og0ij)NYY1zDGJdnltlQ07;WB%BSN%A`nXqSShd-@Uc%I$DW+!>Mism~JYhTxBvv z`J3wwwmQsBEcG%hmDXa;f_oc8WvSgA5#DdY9RYb+UYq*J6f@oPSRe~E<{6a(tXR-m z?8wFO=zdsQxf^eii|md3IdUom7AY7+oYJQ&LLvYYo+|@W)6J7jALhOxFL1+%)%u5P zA_JBAJV>Xl77B$M_`Q)vW3ESF5~Ql~CS!tE*VB!pOy@|^g3qkTmnlLv*vqXdX9Nsi zl`eDic6+DG47HT&Q?vV|{TC`Lj)Pn3%)&zw6SL%}q}S$7?!uk!Sa6P$jh}DW-89ea z_)e3Zj2LRClEg`BP3pC0S?qqCrkXGLl-vzWTjmf3tPP6v$7iW9(|JrSOyi922I)Re zkAr*6z3VQZzZjncImc}JYCnRdx`SK)SSd-*Uu8=!idkyzqeWO|Js^IIdNj4lila7wn$>`D<Am}l>T|iR z*4(}*VXBCt6=DS!5&3jxGvgNimuHdrwUO%0GnxS^_bS3k%GNZ5RD1qu=bieXWD#Uf zl3!rhh9VD+F7g+h*RQV>R4SxtO&ce{X&}Bw)P@Q<{!b*c*%x9?f-8d$k>Qn=4N0@Z zOze6S#A%iiU7Zmiv&j_pND(h6W|`*}LhR)Gf7Ol+)){YSdE6Kii$4b<-^e3cYIQ03>D& z)8j_HqBRopmaBQ45k_{iVH*vVsW*3sLUW~H4NZk%6N!bT8r3NZ$M*33_)8}5$p)qNXc++tX#6fB0?6$D9|;d-MvPJY=Py5;5FPZ z7ZQcKN$>eet!yvp3@RfH#^g~$4-=a5=yr?hhGpoD18I!lhy-IbOnD54^R4u=uyO)t zEj95*{dBA#Dnm|^9G9!!L=4AoNu=MYkh_} zVMp%c141p$LN4(`tYzk6B{2{%He>z2uF@y$J?aU|C7M5C9AoUE@f3zC7{~rhk6FR8 z2PIyv=(X%|4frdrc@t-a;TFav@T6&Oph>Vc+gSwvnj7sqvOGm^l_YL|q{&7nEpCCE z9j%+@s+6lXgF*8OVO0r`x59{#4SF3)^OhadmSMha%m`kS;C8XxY$qyGQ}#(qplai%L#&Y2R5xPEFfNN_%d-*LLY1 zm`soj27mf93pjeN+0ynVSPNHAu^AG-Tw4N{Oqg>^L%G)Q`DU;(` z;R>jn(6cO4UR=M0aF=XgYTf-LpY@Q-`qY zlP`{YbDsk9N9&}&j%?2in-_fT#BPZX6`vtRqQn5_7Bd2NbDp`h<~_h(#=A8_#y|=B zw=EJ8!zpasXV#Nu0V{%Pi|1f>S5xGaKhau}UXiT^jwrhSn8(`iz3kXkx*78h05{hX zPnZR_&=T(EBL=hWEQ)#GetvPcg`8k=0cBnhMf{$?o&m65xNA_ffpFCxH;GHe;Gmd3 zAU}K;?M%Q);)IaehORUFOwNR!6WJ3HusVh#S*wbnLgvwjDs#jEvIykMH#6q$L4^Fd zz|mjE028M!O}YWvCp965OrZfx3V^QQ5VuehaFZXV#a8IBM)*_%T=fOLiqAbQ@_`!Z z$_>2d()>m)0EnK?H9grmi9N{f4@ZzX&hR-s!3jqYCl2WuJq0>v&=}cNUDog*AaF36 zOn$t}^xO-O`7KHMBgSSRl4-BlWIu(la1KJ(Fb87K{_vn@!jxid$Zbcpu8w1#6|5Cx zuuK8(B=!R>X~db-E?{o3z%LeV^hMi~vM-x#?7&mCdhQup&+M-kCD08J;rSKxX}Vkp zI#k{DOr*-ST&hA1%ULJ-DnSY_|J3DH-{h0ar6doO9YwAmoN>6VJ642Jx4R4a(?S&& zg3Bd$%AgsO*wx2@gEifyS0q{?`dBq7edrpKwYj0si0Flw_Yl#>eyD67c^eTrZPI;# zLUL?^xu5*gEB+gR=vLiNQ;AjpZ}M_$^!D9RD#R(}oHK!ZWif@G@R~=#spz-M2>23d z)raVJQ;oSv$um)?6l`RW}9>xdseXuR9-qlNGMOajP3;lHezI~WU=vVgO z(h<*Q-WPKm2SIB9qg1Qx8^;Ux$Wq9ImnRMIveeH01|VKTiP0^P@rVKZ~{W?8xS4M zOq)pCX)5zm4u8$W1IxTw-&%2M*RSP3dLH}aw?Xy=N-M~YsQJm-4YDa~BdWz0pd)5C zi!QI!jSx4@)EWDZ+?H<-%o-?`&v>0}qT&TO{6DIQ{6&EbLdFEt4W|fEsx%HK2o7_k z_fe}*hu&)3m#Y#ya|& zklm3bT3R*^S%sA6#AlsIZ?z|by@q5ha{ERg%cbLL`xwcfA!{jHiuJ--=Y$`eS>!j} zBXYOY*3Pi4B&mFs4Ce}T!i~tDisJTJ!5N3n z)G1GyaW=)9WVe-ZPEvrlKkR=4o0gj-05q{R8*`Ror1uOtjJNOdbg} zU6yg4!gA*va)5}61u2?Av;Fk9!ekn|%hL|YoQ^;_k`%W|657x&`=up=GA#Ha^^Fwh za#ed@;m!(tSaAnCS#xVt%rFy&xVHYNNl0}Av1avSq+q1HQ=ptu^4_(Oc>9IIOoKY- zmn!GiC|#iT$5Oi(5;Ff+Ut7b@l-{l$-_uaPG!e9%nXuKP`pUU{_em*hi5M+{A0?sy zc>)-u>dKEkH>iYK_t)1MJixhNB@qVBQ8%e&Q7=`Gc~d7+JYi|`iy$4|n7TU<)Nwk-*%*T|?kHt9(2TZjsq85V zwy1)iPs9pLhk5wG$7htZp`E))nE0mD=6%FVM<=V_ekSnOvovQ1YDN6vRpo}A{R81^ zbx5zyo$z@4$NWG6Y16EdF97B>^R#ui4QQow@^FGjAi2A}ujqP+Kw>B7KwO7+UhVA+ zROD^tRErJ{>C|I0{JA9fi12z|oTO7j!;<;)Uvq!yL%tUN0qm}T0sx5qn*i27A>AJ= zOVG^T=m*L*{0F4<&nU;l4H=|A$iqEr)^rbq;eSCCJ2z-kl;jHZL*pV4s1ON=@AQ+M zRZ&y2j9m!K^S`6%(oG2&Zu>;=J_;6@8Ybj3So89vOr|HZ@270K(=M<1_`HEqdj)Yh zA|LT1?AB6Gdu~Odt4-{PA~K*kv|C=GMj9}#6fN{8Htn1<-SIasXA!h@t%Ln2FgIHc zW-Y;=t7y27ao9}u+r@tJLTvbh)2h$V5^RTW-DnP+%0$i0R@C=tfip}-xox@0GWB0% zuZXv8X=d)McFZkPq4KB6;Id2|(sNMzYq+ve?#7nJAusLH#nIg>qDRo0Tb5gUEUVg> z7gSJKCba52MWXk`cs6Z;&X&FEhGaSD5d%;mgZZ`8_}wB7EJmtY{23Du%%(3;5<)5* z`&r>C;)=@bgCwUw#samoQ!*yx$Nf16+U!MEucR5UF2s1RMt7p}2PZlDkm%Yg8j;r3 z%_thpYjV`0R-M~SA3n5QB1lu*W}-w7ty8R-yO3T<>MmDaGKt!P*KN!_7uM^-Plo0Q z-DBzUef1GYu=QLWT_?~dfi*jhATvvRg0Vl{3UkHq!~6i{lln0X zUic15-`dKf@fjiXg*o_Z(A%5|{B$7%&m;TT_ zJJg6#lyegY!(X}sG>h`uFFqF{Qe(T!~d&-EIBEOf#2|68O2di zbxX?UOBq&>SABG|dqNi!p|Yo58?>j%%vkY_xjo{45V&7}Kjij^X+YqD2%#G8ac#CI z>38^YbO5C7Oa}z3B7x3OpJ=e@Q~e-G1QxK)C~_*=FiAL5i^M}9ata}yNp5k?+|4Ta zzti&;0YO0!9PREu1QbmmKw=@jU8W~`c7cV@3n)wmP2lBDFn&)pdj9Cmy82mT+_+`rP3t^8CS#*wP%W@7}D1tC1NCMtmdndE|OTz+H~OQTEF8A zTU6IkD3_wL7rZ&lYwKpn8sr5B;NZ_NstDKkpUo#6qm#GW&}%C@7iD%%T4e_5nhn%u zR6KeAOHOft1p>1_!jFB&4;uR4(j)#SH1z-VED?J>Tho7nqN*Ociib(xS!0u%V+TM4 z5PpDx;6Qru@VG$n_;3)qriiil;N6^t!Sw7usig^umR^fX=1xt@Th@PCHrAIP2%4|V z!S$<}LE5)OTCUHQo!?ilnm4SfEL!;EF0MVFrwzenkyv~-U$0zxu04-GrmwlXJ@*%x zW&l-*Edo+Q{qSLT(-5(T8%SOdvlm8EXt?nL@#s$Wsoq}7dAm^uJ8TQlv22+`4PM%L zzX(EY{^A8>zLRO)&7gjfgfexr+kK#-%9M{zLzL`m&JU~kJO0YSa4Sq zeAD*XI4=5r&u#T6ZF!dzs65ft{(bsv3R{-5mHc#mIacp41r? zdr33P9;2LnxpA88Jsvqt-AanPSX1mH+)E9bI@BAeGQ*7x<@v4ey2hO-38thw8L_Un zwGJ|3CNVBqV7qL%P@^rwNe!*JwK)w>spGSt7L(e_cnQO+lH9eoH^mZb>^YaeR6S_% z7S`TxRE-@hsImH()cdFeY$MA|@RptInpwnulhK!3ZlIjUEUSnY_HtEqsaW|xkjH$R zVr(P&F^ZO>^P2^YWq6YS6hD`L4Wn4yra#*jA9b!uwl$0=63cW49`Yu;4vqiV&WC(Q zPRMBhEm^8CDH?^VLWyXJ-eZnR)pHUy5XGbd8K=| z?70w`u9jSWl~Q~>byM7JwphA1Epzb=ybVOUJ&@`&QAy>V>v@>F(N2;sDY`c$t-5Jt z-jL+(qWUnG$Z4IxkP&x_64g3TzjwCyz=W3Cmf;GOqxRVsqGGaeoyK(Z+Sgzdzh~j}(bt_KnX>e_2a~A2Q zNnB5~Ci}rPKazD$ZckZ7Xhtg3Ei_$8%>&Tt zlF5?^+Guo>k#JLAXjRis-VU~zrj}>cLXnD2i)JUoAz@Fiy_DoO0?F6& zNhf^!z&F2;v~pI`JaNis7DS|8Io2Lfp`nICi*M(6&OWoVbFS&Iq%GrHY)F*wD2uPw zxeAM>P-#A(ckfExwV=e3U&fl{rGuCDn7;3K3#l=O)#@PL$P|l^MLaPZjLmb-ik~+M z_LCg|+Eg>*VzPT_^2F9e*21-#asfC^EJr%=cG3-Ke@=g>)m+OWrouFMRV%}$yKh>+ zlBSkxxVgHKvqf&SHC*juF&DVJt3+?ejXSv+iDs~rvl)` zxgCY$Gc>!44B zw9=-MwbG_U*=mKB*;&Pl>!ePNc&mr(54IsoMFQ+~)XTe58Y74C8($YC%{emW6fsBL zbkwKFTB{SB*bAYJlYm!4W7u;CmjXxI7L1@yV)~=9rq_2EpP!%y5P6&2&}qG4tWvoS zn@Rg|2QIjhg;Kwp-8HAoGkm)mXHkx@{@CPFqNdQ6!*#4FYe&|zX;`bpNeOXVZkXGu z!*)m5Soxi2DO+N%{4K9{tj_QDBtK6l^3)l>C-vL;td0WlZ`~{q9|>MMzLJr9`5CLtY9bA$ZGVGPNjtJv1M|X*Rk4* z9Rsd^r_-rqW4=oO6&VI}j+CM?$>LvD@M!O#j@f&3wnZL8e-%L<#IoeRiJhu>51pMs zfn;db@U;>ZJuao94j$QZewIU4;+dQgP(Y{46wXfH5A$r1NR?RTjCB3w&->|AR|Q#@ zu~^_4X!_h~@ENHxiz-f?uwmbdcJ>ZAH~Z^VkMcgAN9x1UUL=y+!GaI%X*_P<8$XNP zLXZq!@F(>w{7F?8ETE^fZXGx=dSi{23`YO!77J1I?JaZTJ z`3F+=rzG6{Vtg8fXP8c7Y-e>@ZQ3C(s|o#g{u^c*l5mSD*k?A53GF?~{KhD9h*O76 zM67#Z5H`4F+or0h6->0;E>YH`UY2>e+{h*b!0rLli^j#?DSJ`-GLxl^-9kC&xB=(!iouuBYg?Ev(a#zWB8pKCP4hv52d5r}&U(pH z;4r1uo)f}JoJf%oA{x{)2{BI9?Kmirae5-8U-raKd^wo|qECj<-b^SQgwZ{k;V;H; z40KzLC{#WyF&!aQ4s7H%&dGCGo_wA0#a1P{8HdyeWOe}COp6)QWpmC4w3ySzps)Orj#0GR08#e+Vncm* zJ&3;%67O$5a_w^1-N6=_rMu@K={J8+OM|<|3<<)e#=sGRyG2BGA-9(45#G=De+YX*qTIp$;GE|4N;Ny4 z(tYHOnuB3|rJ1%fn6hX^CV#La_bz9+A<|_eq0sYzYy>HYi>Gmv?}R713ta@r2TgdH zIyUr3OGl|&@xRM#UnW`$7$* z->4Y>5e{pbxf4t3KH+$^cENmx!>58&q`>e?;{F*`aBmkn;gK%Xt03#&?;v1GM+nO^DV3_Mwdtk-C3zM?`CQHgAm)NsiZ{886L6>t8w~ zcrSu*IrU=gOd0G8qFJ7xDOO(1r2CnhGf`DMZc}~t zGvD}UaYEfv@)0XQ6hG1WMqScZ7m2lGHjY_YlIXq?X`1_|8S@sq@9?!A{g^?LEIern zv=r9;9kyU4WVl~8HYERdb;(LAuRfhqee3@7gSn%S=43&L5x!@9^;B`$HK}=5E}GsJZ4!JazKqTRJg|BY#4lAU$Je zSj7(N#qIo_@m?CqAKJAcZs$bNUVqI}f7ufIwi9g*tHRpJ8@|oX7RjpShI;SpMaJRV zQJ1@{-)VG`G)=}{vqk=P((etV$2ONZz5xc4(4BnhXJjJawI(`n86pEK^``WZCFo1V z@J1P7Bg_O-raw}rgsT(9EX^OArBd_tQWpy_jKXL|!dMCEryj*hGg7aZ|D(;M2vn&fyv7pHr^arcwu9A1%{U=4FOa74|Sl(X9?yht~Mxs3hdc#!Rlk~R=yr4mdBAV`D_*og!PryQ z{oog;oC`;UmICXRTwPyut}+6eh+pMjn8pzO7zHO);oaT-TMew%?|-wM_aJe5EL4O~ zOOz|F0ZU0BZJr_y3q87wl>)0$VM~!&(g`+9ryESZCsZ!20%GhEPol|wTovjMxGjn4qtV#yV@>JBD$ddI*Vlgk~_*Q!l2 zJJl^kj&1xlb#nwt^A?!Rl{&A8I}z224TMm72Cj!@ zBD0WL!FaP`;P;C}PzAQThh`iM-R(ZQ{RB!k!6b%7ViJ|8v?b<{mI-|oe9uf!;;<^~ zCRY2H-|OEw2w(} zF9~s3^s+HI)J~7SRp^kF4jsu0^t`&K^R2wb;Ric=#mT?)*i$(EshM=Ri}TMe(_-m7 zE#hxUvi#e9{l9gY{zLF`CjAG!R>;)!zm3l#)Q77!BWeFt+G>q+lj~?*DwBbqE$oe z2;y{Jkqp+&x{-YA@44}F0$l>=w?@2^*%iB{e+8gEloV4Q9>BWxWTs)fvZ ztX0TW*74fjJ1AIoznHeUuyUnw3^YI>|cFb z{)cU;m+%W53Ms5hO1pZl-m}{=G(XHCa9+0?3V7ATZs115{>0}$MO-&!%kJoH7# zq>0A{&(}cjcA35Bo(AqaXqO#oXM&x&imh7rGv!jlKz6&;2|@-Ihho{3xD2X;AL0Gx z=tj_!X=u_Zb4__D0a_HXu8gHIvqK-Yqmuq_Pc)>e{f!rKp+J|^^<~cI6Ye=k4D{sF zY&vU|ieYS#j+BEUeCQ3Wm&JIeOsY_onuBk!(w>BU94&}gB3R^)Rz{gcc~=!gOA$Ijk2VIc|treoFbQE+6S_K z{)gq+2Ykd5@E!KK^sk1~{eRWh|00fPKzZt_{aZNQgsi!KCac_%UqOu=vsAhww4Rid z2#3@nSqTobtOfoA%~A#@3l*vo5(c}FXh|z|IIY_V`et!SczcVYm?EMsk}l$ao)cBb zd)~)xcZMx%jIV+7O^3%U-^=G|w$F{1#GdD!5s(G-P@K50^0=Kp?I=70KOhW(C#t=1 zDoWwwU<5wHSA5(eWSjmNo+uB+xh#GTrbA|Y=f`%C56F591LRvFh5_=m!Ltc#@8Ot+ zQ=X*XOcbAg^q4-;OH@CI{?(%ivpeJlL$Quz93(EHv*LIVvA5*7h;Z-C$dJG4RH?+B zoYq@vyaU7?-uC5)#~;Xpdf8aWm-6_rU|h6{Tc{U5qPVFih(SK0V<{p5)CUqG1t>4= z@wgD)qmd^4&*7*%YQX_bq`0h!QccbjW)*0%8TD;j8HyTD9}`w<@($$7u5YYC;yU)G zxm{CXg=jU69ob>Ue5xyT@)>KAQAQ{?O2hSaM7vuFmNuYq0?RL%L6Oo@A(V#)br!31 z)~qqdyL-X0n~xllIzM|2Ax|fy6kGa zy4m8^l%b2Wi+d!kl!Ae`WTACmwQ{WNqTw`RwT+*Yi+0y6F6VKn_x3*#~XiS z6WX7Lt-KhP%U@DT9}-TFl62k0YngkI62^K;O-$&Iho{ga^eu-AZdfI%p5|(MK5mU2 zoSX{QlN)8pDOg?MoyBWCawS@9SU(!7;=jpLl=D4~1P+{^`@Yk4aP@eHa z&MCx*ePBuiX|+Tg%u+bHHOU}m(mBFd=Y_zibjn8Ea#J=F>krSPYeG}3rAPt$lAQ8k z;nS1!`nl19ghWk%*cviUl9~hE9Igy!EDzJY4{tdmL0+Oa9CJ(p)=qWKA z!{+X$o=J;Om!zsTF5>!?MHPV4514_o?;8{0pr!VRF(j9Nganvr&dN@77BgkQUQ+b; zzge=+2dab6gj6FpYL8b_ig*X?wTG?W?##PRILcUW)<*4(7oB2KHgU;&8)# z;(amRDSXeIYT&zZHn6@x1iT~Rru0M6WcPOF(Zq5QH91CquQe;gk7Ni+U#TMvpC}7z;)Bx5RA5f@Ag9l-G(zelFTW4eF_0oM7>l4 z3ZTs{%ysI=<=r z98j>&?tKJ+^hDYIpj}&!)Q_`}&)UC+EYsH7b^SSvKaH}M^bqYEi%{|#*28`Db$Av} z`-5;JH9YH{)dn$cxpfv-o33fkyzLSO?p|*0+3&Xq3saMj>|>)uQnLA2bH`MqSQDMt zkfGm}B2G-AD>2Jbh@#PgAvFVTsBe! zGw&8xiW=Mm3Bux(vQw5@j89I`6IRU_)w zAv>MoTe9M&q8b*-6RBEFAf3!hq*7v-tph)3TBtMP#igFWl@~p_${bT4iv(;^^a!)H z%N4y6)42m-9I^X8FhC#)6}(ZM@z0^3#z#ftuH>HD7qK+Bj;t+oi^h1Z)Sf&8SQ>2( zG~vnc(9X$c&0X}IXkkxY2p#HDyW*GUkRx8e$EFXtKq@;4DgBpc$9gy#H{AvLhG>j_AjMyC(&`_Y11 z@ku(E6UfyTleJil;rD)rKrY!K(TM~Lu6U!O+M6)9&<|{q&LI1pAQ4C zu;=}`CmZx|Faj!SWGzzK6)Ia+FvFU^6Z_=k*>lD@YG<4Q&|DptYpf|JZ$}8(!Cd0t zZW{>0;}VS0{Uf$Mxb*E0#<(t*S)xy=< z>`4mS2|?bDnP`V#adMa)NVyqE2{)<&p(U_p-e}LFf`iW&hH{WHV10Q^hj30t@05md zBbQl6o)*_x!{wt;Hs2xL8DGbS190RH-xco4~1*vi6P zY+Fb?m`Pr&3}Vhkm-(>%0?A*FHuL%UiaH!BJ;WIy#};D8on{NHaeYgX3*C*-wWZ)8 zD$5wNEZa|4zZ@5XsPA9Y9(Yr_8S@isg;GG@sl76!^|1t{a(x}wrI+856J*@NUN0DV zeVWqzgLB+ZbQ2gk*Ta zS={Lu;VZFIhnV|gHQx{IBl{!Us{1!r0VP&qYE&h(i>DlTvq86&J8xG~J9c3op@Vb| zI4$t%e@tm*quoA>Ra?}!Zn90MT+812=3tpo-lg=!K6bY!{=DE6c8+*E0EnJS%|dh0 zo40W58TDFZk5yc-@#w5BWS9Sau{xm6t)cj9)V8-Rq$&Oi*t8j%{rL zcer73Avc_rJ?-z9z+bI9jdiqgU!@8yV1e)@A|xNncGK#>K_L zdf?5ZI!@;9hYhqRgh^P|R%obGvXE8Fh?sJ>4oUV zncQV-U5`X|#vcEw@c)LF3(OO%Rlkf9-apH8S7-u&}#K>NB=^_9>$4vx4JxHJl{*3 z)oIRcMGWpx=#z37n0G1eYqft2v!J*GLNz(ythsnAGeTP#r)$zH)eDttgcWN_-`O~2 zT8v^lfgV@5OFz8V;#8PMq1>1*bOq?0xvPaszu?}*tlLZuz3});g7}mmw(5iYiePT| zDb`)=G-uf0s}1GA)n)8B(3@BB#JP~@wnGlJ6*y)qNUYZV4l8IYty~FI)3NF^un>*F zMI%N{bJ~+9wuD&K<=LaxC?D4IqRj_4yp-zh*A)_D%JkMAe0gxJ?Y0~SXJW4M99uCQ{ zLXum5vd?A5Hf;JMY{ka6YA;^uj#(eHUQ)h>o5s*{2AaZR#vEdSR+~BKK-C?H$Jw_eyqN;b$oE7{TD@Z(1|z3+L#aGw+|;hfMJF81?TP$ z!apywMFy@5^WV#C=l3!z{Vy%|&Ss`o-;~1di^=~yl$5gnJ{xxVcAUAWTe(>M17E1R zA&)AA%Ez`eFS~R_-%gap$wF9ENEsL$Jd*y4+?ae{XGuj^Xi3i7oBSP#{31$6GCJaW z4n!WHLniQjQ27Km=~i26e$Af_BH~healWl^QBa#}%Ohy}HXauKK*57jEm9d|0@35N zCyYoAZVtAG;zO2~JX=Xva7q+<>A;Kb5(}-B+R~Xt6;nEX$W?(1K0RTH65?l>qf#K- zzE^)G3?kb8_2G3f#Zr$0n<6rZHoz!Ct%WZ^J(VBh%{8mYL8a?oQqMc~=W;b$Bx6)+ zt~I+F0D~1KMW>1Ax;eb}8nM!5nyz*4CBLrTUvxw-;oL;b06bTQbvS_vRYy5FT-(Wj zbl2Zk<9fM$FtrWm&O&QVJT!=I<}%}IxFMsm8ks3OM)ja@JjR<=%39hJ=siCk7D7NT5j67H_Qcf%4B#L7%T)Of@z5PL2$za z`(_ik$PB%x8}?i$x@ZRlDP6D)_S zr-mk|_l~E@-5a3U=>u}_wo)HfpMd?VE7&8xpRcept|xgz!@9^#Am9A~^Ig_z4L8w3 z6;`!)kGK_GNxky8vXDszj3_1)gcFnfXBnmZrSc1TGnH}pi*4Z{Q}b^}gEz>CX7R{1 zs(G`-R0xH6StB*7YVY>WB61~Z@lbiL?OG#xK)VLmMfN_3MG>0HEm4l3?#U%>xdH{@!N$nW3a z-n(z!(7z@E{de8`SD>PZgT1SfmA%WqgXI3Xbkt-V{*B09%Xhz?G*N^Q1`Jp!9f>2g z-%LbT7LLpU7eU@`4=(LGf1T_w7RL*IF%ID>AOD9w^21rQauPle(k$N6d0fJ*XG zZ#v)9T$c*sk`PUK+&uD}e%x@K`ulxF)qNl0H-_k8Di|V4e6xb42c5c$Ls6_fg@4Ou zdTtETa2gpQm&tZp7hoLqqz~Fyn{N(Ub=wm=9Kr@+Idah6zhK2qcQj_&gojeD6>A`T z%hVlHMsyknta)k7l;2}HmtC#P9w2FEh{ZqXz;SG-ImE|V4e#8ggn)an4z1))1l&D= z$X)uCINMmVgnJOR#G{1x&%_gQP(qs7EF&X@Z?9sV{^5YvY2tUT>uK?&r;I$|ayyq* zFiqj8;yQe;FAOf*bPaent!`mP7FiN6Z0&IycQ>guaqiU4~U9NnT!gx|SmbebI{mS&$l!b6c^-yH!9-T0!y#w_K2 z6Eb1Y&`uCa#|Y5DAYy2n-vk=kDUO3DgbodWMzG)~3<-fn4>2$rFJ8o}Q#F&Kg0;Lo zhA-sXoQNmo`I`0#E#lGqSkrEh;MyTA92)ZAO}Wo0XjP4kMQ#Bt2}iwN-(nisJfo%? z`-?*+9W!=pgehgGWAHau;{yGtMtVH{nwu!4Xld)ptveD{v|V||OL@#zN>ziemZW7r z55B!fx(L3lV64({z~Ka4q`^j^vuYXRp0`J`>+C#|xNtGY8!6@LT?Q775R13opvo-lF3p12dr1ca{=XX{r-ZJdDIHKKRVKKV6L}Z&SB70HQn>9X-g)EPi5h{HbbLN$8>Ne0^9S!OpZVhSz)&*G-m!>J}CE9 zjH?qp!@NWpBw6*#!)a%Fz3@In+(x8uk0{Yg#tMs=e{lVjqND8#zo?CE)_F2}0$wdJ zUm+u4kz%TUTGqEf>{Z(wB#WqE=0CH`ZWnm`rFc59K)%LEyw*5;z+ryHLwOB)-YGf# zgO9cX!71U-u(%0^z9Jf%)HJjVe}uR|*tRuEd&H8yS%EGpLnjL*;_CGSVAoFx^c6oeDip(7(bN4Cy5WxbTe!xqk? zSk~d0kIteHdSCY)?hszjI1k$t6p~O2ppKe1ENc9owJ&w{w0+3>{yq-H0lDu-h7JIM zh8{bU{)*wm76cmduN(vR1Rwf=JJ$4Y4FyMnEiZk~1vfW!3iB`=y~5C|Wmh(OLLr16vtbdo&sVzperd=hv_-YC4(O zc(|eJ%xqC|Woyct7DFm~^BA5XTQUP5fYy+l!$QkL!#Lkwy`V_Vl_R-jB3A7FJxdMUO{mk+++> zN0e`lQw1G4Erw^GaIDgbv1u-inU0jYc-XAQVy0Q+mYgzuH9tkcR<$<@lFd$UyjlxT z9kIDFyRE38ZRwA?1wFmm_$pM%?Kg^7?ysemoHo#bajL6riw<;08&O_lt3*Eoe*^bP zU6dfAZa`9MJyr|aWWo z8gjB_L}WnFZ78&SS>4+mjV2S#zx}L@h_wqpTaRX^%2`SH@xp$f1jhxJ^e~egn`3F> zkm3{u%AR5?h^J7P)1#-)<@U`r$749kjBSWST8czfxoxX0&F(`4Lw_TyY=ZfucO6bX+?!_LzP_58H>69-#Hx6OQ z!LZrnl-aqMG+)Wgh~F&)B#?%&#rS!6V4R=q91R~RV89c5AyC!)%3E`U@n}`%0S@^o z2(SxJnCIpFGjqU2*|hzTA7JxKAwNpo=N_{p24-v_oV-6l7;-6tfSg;~(=)1I~i9*ZYZ5nGp0YgftLlO#s!6Wx21^bw%{L0%4T_iZDO86&ZYn zV|ad|4%iZg^;6Y?UocJ<9|ao~;+5T_BA*rDhXDHFk;Hjy9ytf7KdZ;+c@K&gvlL3& zC{qZ1|4vT!5e;ATjfYCW%W;(P(Wm)q<%XRo>6Q1yNdR?)Q~uoTBIsH;Xhth5ySzv& zj-I=@ldDcF$ru{au$b3Yv?;eQ)b7f!E!EajT+{6Vm+SeVMpfVV>%bT(^oBRoKJeC= zzzhI;QX@6Y%7t_`kO5!6G{5}irp}qjW z2l5$lHurN9ZR*v;fG>3rtrLflj~9w}eV!F)1wnp@aNRYhq?8-79s~eB=am(A@vb?v zJap)jLWT~e1n?4*^>zFGnbDSZn>P%OrE$2iw-I;dGyZx;hDJ?Kx5CC!1+bx=TjMsj z+h?!urryz&*0*2(mbi99qu*AyLBX-kvd5U>urbkeb52j?fXRj< z-bY7C(U*eCMnO8B0trAFiuYHC6AC~*BoVerF~8EOIhm4_Rl)W=Ms}dlSc2K@u>ZD` zV1eUlJy>tZmOhm!?M-5d;SB2cGmi!q*^9t8Exa(-C&N%;!Ch&bm@}6Qv4M6iR7woK z(B!!v#{s`h?vQ`un6U}X+}xe4T&>Iopn;vriLJ6UmfZWj#hWR}bVJW7S^%)f|60h6 zXff}NKBRWmQZ*Khs#6Egs>zUNzdOy$XM>8gw;qyUQBQf5A?bZzrN^=i#FdMH&8*@s z8K?8fT%?5Jnded++`(5$^Q#Eu@W5D;4NxsPn$Fax$#%kq_4~_I9hS~YxoXeYRY(lr zEX7va(vN$!y|7ZSVzNT1%5QGBqPnRcaTFdAD2m`X3ztQzgel6|-?Rj8^$Ft)eV-gL|YJ*yEyF{pdlfW+QbR@z_^?Pm68t zj%~bMR5@E5V9DYX^%w8@DXO9<%)Y3rFiO$^;nUTGTk2&&VtwL{dwK=AQ_?lmlzG zLa*vEh{0*c)Ds%Pi9#^?T?v~qxo0G*?KJpFy;r{T(y!G4jrR%i5&mI<<8WfHeS@bP zf1sRV@~)2(brM(=MUOCEyNjV5V;l(NDufnU5zrTuS3##TSD75fWjd#b9FXj47fXhZ z$g}ec18Dy^ps|XO&~tjD`p>rrpcWGw} z?`Dt6lOho%a|>gM3V>6dFsJZ`6A@F2r`p}I6A+T_*V5eL<*W4jIpeuw;l089bE&@9 z^@WRl&vO>A|01`|@xPbG|DNamQK~9t7ItR#uK&C_s@tfbSfKjLHyRPqdWxumwiH4_ zAvf2cHK@iwTlUM)Qn$9qG{Qv@;c&pH`Si(t1?8N}mKl5=Vpv2jyO%Cu_5Id(^5A@a z=+3Gih(zSck$v`ix$59O%|7Mp>ihcrSO=s(lBU#b)cj-1QG9HJauAS}+!-y#6zgK` zp+6QviS4MgQ(V*@?4r~i6G2ITB%Rz?6_Y;PBHkV3u>#=3TT6NUL&-j1JB-SFk*LN` zi$`!a4&}Nw=~)K-7+9-1njts6_2}Mue0<8gMU{-qqED63L|GP@;t{!;Z%1%4yt|IZ@3`jG>}1IiZkU zQ-47#IFZ{T8+eV{ZW67nutyPFo7}_-tUAFr)soif(5l*(TPN$%4F>m+lB#bbzX~kJ zdqoNE^3iM=?|z^>)8hLw(x;v4(WC!`T(k1kA81t5UDE$i9dWG19%_)6$#gmcI`a^b ztoKm9mi#X$}K{ApGiU${B4z>tkP<`>2lidW36%~J$ z|2-tRswOTpvz(e&FSQ;Ht)f9Lmo+liLvX-e4b!*QwR(c<57>j`tk;k@7@~uLY%S1Z zzRpQ#7C6GX8ivIZZ`m3O*k9y#gUHwD9OJC za2^qOE7vuJIt>J+GR7_RL_M6Nvll*SbJ+3`xu!{@WQ9=a^F)0NN_E7>7dh0lnXP z1O(5l465^5voWh#^K^%;U)Cq$tB>Fn3CFzzU16j*@T3Q(!uvN|iE;HKJirkHZ`&62 zn~KC=i=J|;N8Iy`u2TyKRKhPgg4!`pM&=5oWt*v~gimM!HNnYo1`27P+de9FpKEaA z3&wpgj)Ykeexi297UU?DqbW0lKrfFqYbZ)+_Rg4W#s{ubzy1K}!P8C!B8jUfi{6E| zzx<@*h>@-ZMQHd zm&d=auf^<5h25-d|KaXR{eGS`Fvq^aY#R~P?}UF@B3V~~1D9Hh!Knj?Q?QYQlo<6> zudvb6NOwRh?Rr$d9rFyc%|)#&emWN1jH?;)2ClB9dqoC=1tnii5lqA_2~;fEygtud za{!Or$E&xaTLXWvdX%<5>%T~-jr~>kCD~$)72(HJVc#0?$NtCJx)Yog)fc8$C7H7E{}odWM)oofv!Klo~mdoowq{5 zY9aydyePPNZcUdFt+j!h1`S^Sdyki5=E-$&fhDO{ros+tna)D1-$pqkH-xVK-AtfL z+}ZnZQHk$}yvA4=%uYza|xd)8e_?I&| zLm%^nqyzUgrh4T0WMlnrsHMMgWL_^!6%*el6JpY_deroEL+XcV0T}))Zw6nI-lJ0K ze^Pl}F7@xx7d?xZRt z#y6Xe9KgRE0-e)DYL0F#vN%n5>&Spn-Hc_`X%w$cZdc-bRhFX8^ygji@}Wm#Ds#e$ zB?U(k>%>glu3adczf~_(Gt-ch^=@)J6?Zq-*BG`Lc`EOy)tTAHJ#ok^&$}(ppt&Zx z|4*Zjafa6?fwzpwZ^tB1;DWR%;nWy${m&Z&wAT0*)?XZ6bL&8Fry=nr_Zsyr-Fl37AiUWSoA?FBw)nY42LrKTilDth zBw%&@4tHWHt@(@m5WmMp1h#$_wvPR7%uT~I6rgqjx^#?x?rgx~gDhD4{Z3Ek5IosI z7d@=tMYHEs+$Kz_Yz@GT3ZAorWVvylUClpacvW*PUw>YfOPygPXgDick-k_z;8KNm zpDu{}`ddz1sE_m;42|QRM-g9gA*PSJ65LP8K{4hXRIDW4;5rIAZebqX9^pN3m&?*i zDrcxN|90Z=79o=B#gJ_b(}XjW8driY)T;wW2#6x%k+Mi2#)ENmd#nM+!ZU>6)dKfO2m~ZrKC!_Z!D2p#oA;?--m#my2Dm#ThT2D}1x{<;VA6DX z32YLwl}Ig6$EBni@7oiPOUA1>X?_DuwB*MPcXBPs;PBz9ZN4-~f3#C19#DItBO;=~ zSSmY(qx_juJfKoL9vva01_+mN?3-N-_>@CaD?nBytM8)9;CuH!zLs=;?BJSYD>Ne4ekk zT=u+f*ZRQf;V?wX$DEi_(~&@m9%O^}gXn4~V5372hdt3Y_k%6ThqL4D8?lolBc!G< zbyQxP)1w2bMaLsD&{;^}^N?JWJHIm_X!$8>=BCy$ihqkS89*OQy+#Jqh0aIb6ZVqN zQH=1Cotz`)4m1T`a#`^??1P2m=U_Iylpt$`w1A+jMx$lTa!suNN}Bmi4eHKGMX^@q z$ji)_nHxRB$s$jjCBI0KGdW7`ypf)QD`Szp{c38f;LV*<&s-L|2=y`Vx_HyGk<7%y z$CcVpI%@I(J>jzzOZocjoI9BLgTf3!P9b(ZV`x<}k;Dz;T#3eOc#?C`1H!MvDJ;Eh zk5q3A5VBi+A!sfL^Mpi=}mBwg{SSh|A__IG?TLWw$>Hx&@#KUrHG)>6_x%J@Hg7 zoN|YuKz5Zht&u#c&mYlp0(Cy75`10^@T9J$nG?7owT*ovc8X|geMkuuKwhi!)!KoR zS0JODcqJtBkf9rPovr4h9tu*YKgxE+%2r)}@0%r!t%xRp*~Fx(zb!UFm>74A)m1HLX@5mh^HP6-B)!f3Mf;%pva$ScCIj5=HQq^Mz?z4v%PhZH?f#D-|{^ zIh`5i&sYv{G3K`?j)jopC9P?5-0k;})$R9?^FH0Ij(GMtW~bfkQ)A)7Rc6m`q5g;*f5Z2MSyI6`M63y6j<`1$5l$U99__G=Wgq z0jv{Uotmm8;B|$dmrpAj&a$1l&QCXJ*DZr4G$)>HBkYE9avK%Fy=^pc?hacYq1#Kl zItK>ZZ91%KkRw4dxB=gW_vr z2XOFiUw2WS2gOgH*BAYuTgtq@UZ5vNqIE=`uJ~$cNXT^;GEXmr;yPmt3i%jv>#=m} z1Ft>}DI_%^>Fp3Nz@05{FHA+vHxe8cFrnA zrH$b5q6wg#ecA_%I}u)s9EwhisZ<3K_{)dV!5}!I6SIbLeDip?h9?TD%GJuF?;08* z3zUQL);nb16#>TqW=k(A;b&!S)$_>{*BT9@K@!v(-`+3Sv<9Qpbw(N6t3e4Eq!n-T z=f|9*9jqCXYsFKwYL)a6a`p>U|FCy_gQ7T|9El;?&)ystm@ms*F%ebN{d><7q~7RE zu4*dSX%oJ#-Cp!BFx%)>$2RtP9!$H8u~CX1Yx@HO;I*oQ=ls-Xr@&9t)k91+XIv!B z<#EWpIOIY(;n5DvFpnKjK83p(&Ywx%UVg#5!Pq^N1a0s1SEKl&_=9+=T7By2D%#yg z)YCysyGICj9eG7Kh`t!1%#QiLxq0DMIPz8hxG^6lZ0!Kf@I#>&Hy)n7y8MJy;`;s{ zXYUjxTbOO@uC#62wr$(CZQHhUrES}`?aY<7bEQsJ)!yfxT~D=dYw;BEG9y~dIsP$5 z?_Zz4L0l%GNh{|!@|R}$GrwjN472M2+;&{&fC`}3YG=EDlWXg@#unw@y1DBJn;)A> z)mEr=QAZ&im_^y{kx8FRkbVL`^1xkAcPrgB_S7TH(k<<)yU!`72u`IpVg7BIUxR`>I$ER$#;(YjsbV1#h?Q`0%sR6^JlIw(_!&j==g4$sMELE$5_ z-4u97s2TBmMDM6ba$z#rkxgF3{t6f2z@wfQN_A2S_rM|T1dIiorrTREes2A*|; zCLQ&KV~_G?UbuJ%e2lHPvm)oL3U+;X9+=-lPM>BazG2EAAd6#0B-x|l z@n~6ZNP*lP$7uc61%P;D;`zmzz%3a~+Gg&<0|ZpO&wBr}fqbhqqZ;{fam4e;gQ;oDKfVq>+=n`@`~rAMN#`(YAn72#yTos|depwkIbvL_h|%Sy%x>9&WYj zq=;JWJieBE#d@T>rQ6FG;-4FOH9gQf*wv5b^s0szV(H_#oSC_v@^&?S?)mz-h3Esu z6`7C0(wU2oCIHb9;f$nbR9J|Xw2+n*g+6Sf2-8wz%Rgv|ln9-{Fo5z)B}kU8rVmET zhBP}Jhsr|3YW_KQWROHLcYBmtYf_a^9d#5#zgrC$jU7+QPOrgZ6&!G=4x+_gVO;c> zvAbMnI$E@CG5Xk2t5$f=*G#cpMh>U>EKVZ^)bU*Zg)SWTdn5HyU<*jwOPl_*_nd4RDqg6YU)T;fIOKO0lYQm1dT3Hko8Cjk8pIXc zCMF%3ck%LPbsA<7UubgdEpa&EOhGut;G?ZaWy>I(mJ6nJDlNE5u&kJU;=pBQ(II=F&UoTfBTQ5vQ;os1}*P zKqh8U3s4skbdATvA{MxP>c8fu4RbVLcH;)&raFFbB;8^0fwSOfjCyn52>Z*Aw49Kh zoQ**FcZYc46HQoQ3u{-t_X> z>BiQj4Ao)OogG>E-_s~;!;b|vB!D#}FCR$kQC7*jiXtT{U{MBl$Nl#`<2Mp^|K}}Y zU9zTC1N-aOBjNvU#-jMoQOiG)8eINwbV;}RznJ>7)SiX=^$@`301z1>Q{t%L@c8@! z;^33wfJD#$bZ6q2Q3Qn;Cd>oyc11FqYZj@T5=h#ku2^K2Tf{9$Ou$KNvT1EDh^;a& z)Gmmb%+tA~HrAu{|L`+Ki!zEA`szIZxLtR?Y_b7;D4Z3AQIh%IWsz^@k8ve~VYK>h@Ih4SnGC+QZ5Rz86+890&O7@Uv3eD+Tya z`^C3d{c}ORL2H5E^Dez}J5d5%!D-DeQc$(P^-5x{2WkRcky5B5QS+2_!hz;Ua9EA} zT^sKtYzTO8XuYf=9Va9yje&^qh9IYj*H1DiE24oP$sEig)-w~@ImMK|8Kqe>tEK|{ zsG!Bds0t)v&h;e4sniJW2Mhv6YJQGo`^J~X*80R4crv4j5ecD4X9=O=-t-xf#c^;f z&clfFGs2vim=VvVN#$382m8)y4SWM|0)tan*y1CI>mb@!F-4#tShj5o1O4WX=JY5S zygPMi-R7Jwp0&OX#e50a;j!R?OmtWI#%{*xQQY+e{eRC?nUAUt?cIoTB6AKp+8HE8 z7v=+afPy^DEmkAO#>IhFN3b-N%E!)X*2FU%U8x-y5#wOcsX&?+bC*)3OF#`FY$sDh zxC|rIJ4EWrU03w0xTtTN=f(TIUF2s^<@K0d$QRH~#3KT6_^>60A2)Fxi{nPkM^>nE zlCfh;2>-6fM^W0LblAT-#^CHbkYL!mJn(%VlHtu=!m}nd8-9}(uvSd zBOgT$g%{K|%imG4biAP3S5Z;VfyX6UJuuJySUtKz*C1%swUSO=UAF~SRDnG5)=&&AJ$T48IYEP?zlJnhS=cns%W9z^JQWe8gYwd1Kyo6 z>`%Ne7iG_eL92_(j|U)M%fgm<65}$=TtkzQT~f1xtY-)o1D0XhnFdSpDI_|hQ0t0e z+hYTMYV26I5uOuchV0G+*)+uyz(H8PVV+Kc#8TKYG=d+E=r_4G#bU^=n~wwNUv7i~ z(xNnJC!w+~Pqb{AhNP4PjO-0iSx4g=wZd+Hav8@YoQ|I z8;miQ5+7(;lIV`zD}a~YM}Q^3L-9Zc1>kqQd-z*uY0WnEh!$ASvj;u%VnX;@Ds zT_3=bj7$hys>vfY|%GvN7V*;t_V>~wW@t~%+B6@6wiFAb`W&6>Y#V=6qnwigSd z;UcD1*MWzzUZ5_F_U!d&7z-eic=%n$wH7D>Y(`j)Y)0L_*kl?t`wv{47?~iFD zE1+3j*l&>Qus&0L@Zq^KRE z=~`wHQr(^KuZV?(DV0;pdX)E(a%5)EABSn#B4=A~3!j||R7p|-gu+I`yAwC8Gw|Dm zz?{u~-cUT8AmDHUAy8Klcl|T0sNPb<1tlF}D{FuM9A0ukxI;uLCyVx+R$k$;Nxv;u zrpYCgkmUwNdoaKgku=Y)V<0afe86NEuT$>>`qyef}4dIzI2=qxW)4Z^Jul|+r#;@1RtJ6)R z$}tNa8Qwz z-Q!FnLb5aIDKZ5wlDM&qGUZZPek?ZlssIe%SG@(OI5sWAXqu?~%w95-BngV*bpf~1 zCvII|pl-jvb^7S7jyIxhO)j5;qZWY1`I)^?sl{Sq`*SD1k>=!D?}`!YUzP#bpkaPGrN9Uk)c z;K1lK#-Ozoy#6K#o+blcr$VgjQa%t?9U9l%^gM1?1fo=SQo;bZzNaO??_+VJrFprX zNu5acmk*KT+a?hmgWlcW{ReYM>ZdkSdCWm18!zpj(5X9N`sCiYlLK)ag4RAe&=axj zj!C@JmqYpSD!k4cM{B3@O4iXR3)fVhG_-N@K@L#%8a^1l`9U)80S3j5brnfdnanhW=H^&mVrYJo?vZ!;9=EGtp^W5$x zM|WBFq135PYdZZk`!%SI%yRZHf<3{bb$$!DTYqfEolxry=mhW$0p_a3!X>i6>9gQ6 zZ-llPam;!_`6ex(s_wO#r!|m<=-3-~1czFL+Mnf!z^uqcna8c_V!$S$L@7}NhmC*)L6I)C5^mdqiqp<3^Y zSSV;b{rQNQkt^*zI@(Xrw2(?kNv&agEV+EjuyK|nf_N_7vPI&9r1`u?M`q(39qd?c zy8{rO1#+8FSCe>N&|gKJZga-_-ALhjRLC9oyih7>xXCX+A+u<>jrZep1;?XqyQY?L zO^d$$v}+mKjsY}SF%B&pQ0P!2;3h+&(+HUt6Ja6t!jTL00>j6#i;QVyCjqx;p&ZhZ zKmIz{Odn__4?YP^!OHgWO!}}qTu+j;UUxuq-V83(t)GVmY_<^>C4MaRPns7@!5C9H0f+Sel?^ zAq%>$zq6BRS2U)1sxI!is1rSJE800#w@KW_{-DVDlMkhKVB{QzeudNR)J~x9D8w?% zn>+ZI>zbzYgmx^CY;Gx{5fJf`3$ZfY6?d}KH5IyseK*ckml*dBR0`SSZ?2+Q7M+l@VZjw^7oO(g&*?A?e6UY9&piHTsC_F`>|?x!a{E^#XStCPlnQSXj?U>nT_9w49I&^fcM$wA;6y zf*(54OS$7)=b_w^`0Qq-_*FQjDx)(EJzB(6b>RD0s4*&3FV@mGqS)OG*F=o%W!@NKMS2jk(Q@1noS0z8WM~;Hf8@hL`dSTVOvXy6zh( z2oft(FXJrBw>22-H)6)t9zh8;Yn1h33{;ai1si}K2#8`FOnO@#u5-N}8g29L>m+11 zt2_)$6<7#gF%x2>ehy)Mnd3iwR_Uq`b2I2knG-e4*E>($tAFNob;m!0MWxI=$zY-T z&_Gi?LqIj?p-x|i_{53ij%+lp*7P}~S~GKGYUB6IqOOy%dj_FRY-J4150;{c2_d0I zinudR2%g!eoIu)O6)EY_uJsWh^$M)2=V&-Ok*&N``je$x%w3aNk?a7os+5AIbiEqP zKpsZ^z$ah!yN`2vtpup&Gi*MCkrPQYG|OVT`rpPb=fuHBRzFlH@gMo{|7J4z|NY%W ze==k396kP}8ctE1lpWwl<{7NiR6qdvjpcSw2~h(*5<*TTctAuRg=vWYx zvCCK-H#rl!yy6~s&{7AqfZ>t{^0N=mNATX|k7Mt6GqVG9$v`YNz4Cd@Imb`zp&ZsZxABTVJOk!|_2QplhyON1ckRxmz`W{`cA;wCbn0yW8A zgw4zsuGoYkK#NslhAA2up6$wfR}q;^iYO+X->z@rX7&Ceq4(crX6(P&&PJ@FvidqP zL38P@g=%D$xBbAU9%t;iR}XqltJ`0mUt)$1(*-DtNuLw)TP=Ek(>2I6vIxVtiGK59 z2Xzbgrj3~r-U4ehtWy9?suRf3p|-QXWzGS=Z2?ED%ma_FpqDc&%Wkq{|y2J+YdVr`V$$U^fN-^`TzCU_wz$w!(Y9C9rp0FigUa?`bzISS`c*%uJ zCpprK4d#o!?GHO#H_u(0-M!o2f7E_y-3GxuvDX{~M<($iAGX1XxVHnlxQmo2@ha9% z6jYwx^^-v}423Wry*4ZU7(`H&M0(+(>^H)IL4zB-#SfAr`w^87T@=z36Q1)yF*K}4 z2=YTkPQoJ<@KlYYQx_Yc(+*$~{FH=u74KRinIN}R8EuPDfC=l5`0dxQSr5;Q4atp2 ztDWokl3<@P#{+hfdDqaQ6C0)Qwu<{2H=CxWa6yK@LTy26T)!0L5g2HQIGN{+iegWB zAabc=iH$r9wB#}x++cMwc#}ZgzS8=S@)8zIu%s~{c#MGI*753hD17I9MQRfgeAtPq z#*+gr{e+72@K%D9lfh+wCoOL?8IvABw!EHzu1QRc6^%HW0&7HkG_^vq&Zq$l!xYL) zVLjT5^}BN&bc=?exDk_Vg(5I4Vt}MH(oaBq)0D3C=SL&7gmc&o2`ezCEmS{*Vxvxp zgpS)!(@@&Zv`CsEN2#L4t3`G|Gf4-l#LX5Abd=Lgk!Yb^#0nZf+&pPwH%C; z1vU&~@VI8CSfdq1Hd?c^pkzg$U`fHouHS814HJ3BC5TI*gg=+*9D%s+<15k_5tAL( zUuvqz+iF@6r8po;PfLBknJF{M^V4xs5;K<+MXP#;B?*!=fzzsn7M5&5y%mG0BW(z{^J z90rC)y~_881{Rd;Bhps>=$=Mjg$GfzRUBmcOFULPLoeF$_m#X$^dCMBi}szo^Y^8; zmTpmcR_Y+Kp!(yJK0$XYUTfmDIR%z(d2A`xdiwGw*NxCD1zSCXz&!tA0J2UNc2Dr5 z(z}>0N9f$@GSTfY_9mosLR~H$sVK7(Ih0JFf5c%?!NkcbHOt9~#mqlkE1N)G?3l{d&|`mzTS+i0-pYqVte$vmYViWrSp zD7Aq?Jo?0xB30_r`)0f7cQs(7QB4Nch#PeZy3_Ah1o`DOVr#M?A*tm!7I{%#uf6wk z7GlD|ktBBXB3;dzDV@E7+oU#$zKUMvSp17$a}PEd!6KXonF(bce&(<<7sKKWy7Is< zUQWB1lpMwk#%mhZPO1hWn|8C~)dr(Wr>_l_&jv+P3FV~9P+v_qAP*Y!Z+#YJ?x zdR-cNT{7v{us*aF!B9)0I7>_8TzM$(0Roba4MBUW;0q8KN;t?I0S|qX#p~ZhZ#J?| z6n|!zlQV!*cB~y?3ci+Yp6=M`TDX3ZsqWc zZqAa2$uB+`e=AZ5c0eB2fIN<#8Onen(h>ZIAwH5D2OD2)44*`G)g9~t*_nkyK9!9_ zenkufqSAHnbqSA2R&B9MTjEQZ|F84GaLVS7s|Y#tTSl%4 zS_}#$EZH zd>&~uht9k*qHWi|pwgwWxY%w;#!Q9kChxOM*H627`|l;H93cEvo6gL&Qiba1E=Qq^ zhOX8WsctAVLfH61YCcfb+#`q$W19$fu3Z| zaa8fKV6h^Z-kDUGcrq2aIl={nEX@R;a^pW%=4G=gc@V_+Yc6aa)^M0G4O%rc{=-Oh zd)Es!U5d%-e4;18;W!RkL(pQta#ip(k&HIy#T~`OwLwe9w|Nas^%Euzd2J-9c?cNC z<)uKLKAdgMP@9M51BpscaCVfTR8|EFswF6w5EkXKZ{0lWZ zCbtZA9+=2qlC-wHGQ>Ah82kcZN7F%Q(J!KGyj5I^E{8vg%FV>Iy za27~pWkV2@`-Md$aoGCFgV#bQZIe~DfdyfwZ!~6WF@BDnRJyUdI(q?GEdOrX@dx=m z?fPTc>BRaE6K-5=E$nRRgbbXF42(^T|BLyl@{c$Fbt6=(LwF-EV|@1-vnFjzBi8)V z!Uu-mzO)Tj77(vMWYFKhmNaB;L1pa}Lho3Q>BJ)5=v={F$$joqjUwyDswTy+7NgVj z)O&78>u!EAC{OV7^;B}Y-@+S8Sy%DkV3H?_L8}T%|~s`XyXaaXcG!|gZxA5 z(XEB+75wmibm!~god-GT5QY7g$IVMziwC<;^jg*xJ9&a9{~S+c<6YBB~TwQ9C#!KGo}kf9n|_KflQVv4{{U+i zmNa3uSm;Q)+tnR!m#Q?4^;QSf&TzwSxBB#KbH2H>)6?F0GVnZou}N#IwL8z2m&b`* zvHIXhkU;TBwovHG(QQZ$EXs6LZm)YZ1j&+G^os=RqLD%?zh!ZJZM$$V$hC%!A)VAj z)r>Vm{DsUhw##d%FC0op!&P{H%PcVgr``#|!)8U3wN~yZDIz%s5jw0PLyC#k7-iOh zQFfm@EFl0752TghOU>2BeD`c1GVzVXVmL)F)#UeWpVP}5+9c_l}-2fq`us>M08-S9vP zQb5Ljwl)IWE8lpMDrCjBxC12!o(HZda=j*CKbWM~5A)znOpGYo2&2+}If zOwY5a*dv!CxlyT5qLc2zhJ&?V&l6A$7Y&+{3ZrcNOxU!n-OQdVNsv8?e1%gda|`@T z#=6+f>aoMDXb+lebF%n=gCX-sWHu@=3F= z*n^BonR_e2K=o+bTBYsn8xyzKLaRZHQrigqor0{F=fXG=CFgby+NSxdxq&PF?5qjR znCU7f1RH5Tu)aV#iH&_hVt~vNk_s3yWaT@*R=o`z2X!T1 zOI5G_hph-@Ri|q3j67%4u5DPQbaHx7PJE$%f+1P7ZVBo7K2yYVF!JO`L$k2PpVeZb z1T!v7nnc44y`g<4T9I$xoYW+;rM8|z?G2;XdJE&7B(gQodRsPNQS5KBzxf&~_`zpE zN%+`_+Czre8;7UKnG3^%@kyM`*Gy=lBV#@rmJA zf2-IvHBjl$eW^I5cgF^0*B1l)U@Y$*D_sZjwLPHp8aB{?IHScL?5F9D8Mdw-SDj&f zNQOy^{yoa}qU3H_0hlai%vv5LT{+i1RXi1+t07Ic%ge2ZzIak&|K2m|XIpePBovZh zke^>O{8?D(8=fytp3yOEv`LHWq0B=^buQzZ@IeWud%WRBu0)2QnrB zN~*@{A&Da+FEaTofCb*;>$@==_bJ2RXno5?`))FiJ3$f$DMZA&c5atO7i=ubg5+6A zSKFf@X@f(l0>+6D>2#{7(-?3;=_5X!_fCCA!D)cPQ#zvCN0VN2@wmmdzWGt$YN^GO zrDPiiXh;Ktr~N0#D|Wo5g#fo zbF^E;^dFVEZm5Vm2kIe;^VUWAJB%+vH|-)Da5V2q6rk`chLlD zk}RM6WYGii+NtU?{JgJGB_xB}kWfO{KqLC`%W`l881=T=3Qo|$Lu0M^WUTc-sgJgp zd{3A01Ko6=7to7O$QV#&EvoTs-s#p~^~oAiJJ@4Q+5{S~T@Z+zwEAgTuQKHrjMgq< z!349#gC3U1+z?8@3v;rKJvdTmi`pm|mBy1nBSPe<{@zDj`B+3jEqh*mcUze$V3J0Gna~y}0QphE(CIuNU zDMsebZzI0~TY^m(bpwfZxdFlygshOl!(vxX1MGZYPk(Y3UgY7q06HjS71vS7oh_@v z#QRfvrab7TtW56NsMY>@3G)u#Xv*bx`9a^6(kY}5El{9PGH;5N7JE0-wQRgVg&D?e zBN^M>yDb)kP#nTeMbd0Vnb3$bTbD^>v&vfPvmFbwaJ0;=4dA8qki_fS#Os>L`$AnP z<!xaf*-P#@&e z5~_2Ed>P6*=dpgjrO-vP%sJJH@PP#|l6<+kMHO0#+ zJL!zrFeDF6yaP|Y8ITVArcZ6WZw7wWPnEZFBOo5yNag3oUJF~)h4;@Ij9u70p;D@??8cwsAYiH^BQa)|KZ zG(nUZ74G7Ua?OpQY{qn2!lah3E>pXx6Kpm}+33cAhPaAKxKU{XfI~e{t-}W*IJ9plc0|VsTWyi=ZDrQ+6-bg>tH+;#K;&sdj(tIhTPB;}%+&b2|(#ptu3tgeDA_t9f~&d){hhRy|VQ z^v?yY&btQ~06B^1!|Sa`*lJACJT)1kzMK3Z*oLva1#&0aC4LTKz?{VFzE79U+hL~> z2<(9s*hLYTMIm_Wg^oLW(lxP;cUQzEit=y>0;ZFq05lL4{xF@w7Pk)2{ z&zAlVk!M!o&zgz<2Oj?)&Hn$@)cg}x_umA&|F@-4-BiL-!Ty46r>2%_C$9#WH?)Ew zU{-8)w6L&9M$H5!M=nw}K~imS9v_(wt>xQTJ`EZXi%Vn+RQ zh7?(|)WG3yotfWB_Kh1y?6jKb*X(y+%l18+J`uIDYwF5~(b2%3yFLe2wIJ zW}pO0#a6CjJ^_VW%_KoFesuv1Mmx`TG{)4#erV*+-mNVHgoC=`6dO6W1MQ4#V8S%oFqJHEmcnH15$t_vBC5tVaQmJq&d(Nq*&!5gW zqM3&w-!T9lfJ`&Zp+wu>#cJudgOBely!1w8D8JPNHG0Oq=!*UEQ`K-vY;w+GHOMx@ zpRN}7jkplNNH@Yk1o!fG*Lizq4jK2gp{d;OVjZ?Wh<}3V(m@t+ki>f>SE+B8YTCS=T1$3Mh1E&J7F^wVAU1j;D@0rd7X4+)oNOegCTUV?{ zogY}L8MUR!B{@yKg=809x!*`Iwp@Ia$d&_B@YH~Z1 zR-$xO>%dW{8wF9IA)0 zs`q6$ykqH@ANOcf&l9w%=6Y^MJx1wIrC{_7MS^0h_;2zv@(IFJU_g5cMwzP!YWs&G z16^R;^eNWnN+g{Z+}4}oUHII94a3x*v~xo$Meyk_I%Bi=@1iEvGfnL{S>MaY^<+;St0G zPIHXn?@Bit;4E;$otEQebg!(arsUFU)o3U&pgsAd{^fiQQJmN3DLR?%PHIdsk-6zm za8yz3tM0A z;uA2(+E@3+l8m;*Y@L6+V3HZ706;GkNPGQZZn#E2kgLI(UkT!|^c5EQ?kgzvE=`1L zFH=$%pj>ZnhIBLq9WT)`e`zfQ)aS0*fveeHqCrbklm@PlnPWkS2BI{@1^*=?prt}_ z&W3Xf6?(}ZCdwCcuLg+X10D+U`wHh9xB%oUR2XOxCjElXNql#><>hQ!L52XGQL*QK z_(ij90Yb(Ig!l<>XYKKo7M8PjceEC6}1PSz#w@gmOX=2#06@fI@2|_M|{gWaGJ*jCLuw#Q^`?} zz_*W$*GWz89S4VT0BR{tKeavdC*m){TDh9B1^$A2=x{MX}7#n!;lCJ26IY{!ofV|8fdD>DYFXs7 z2|KeIahAj_S{(14n(OtMdSqC9R@+^)DD~rfyt|rDApzS|(3~}YzUDBSy>9h#eZlU2 zx?%GJ-&5_f-19|(G!n#xXQVxLX%Qr*{C1 z!*rLlH00evO8V8U^|Yd9DcObHCTKccyjE+NW&D%=GHg!7qW9f z!;Y5aW~OtIgl<$vIWhA|iUL6bhuGocAaKlc7-U{$MoHK}&n_p2fG*9cp~21GOz3YJ zIrk4a8WL-9D3xMXhGQ7)vK-rtwY0k`hszpG2Lmh#VO{nJhe}HY+yf-!YdP8rq||(p zzIeZMhYNsyYV>C zPhPBAB(iW|#MyBR3pt`(>`d~^8a%Z^#Z(#fr7^WK3aQ4AT50AF@&~An&IvR4^dm%j zF(l7n3@Z9SIFs5f8au~vN|eTlKh=}7iVw&Z^_@!FlOH)Q)J=b02=F!HwJU*F2zUOR zDp&L8a6vcz^w>a5+xS3d@FV1UL|X`#@G#Dr)t&^D+`bXF6qMW{1{B_CBP18*-R=O{ zZMNX7W<&oS#%odfJ2I6ijRnUWL+|Oa8FEW?bt;6>-jDHMR04=j{m0%6h4F~RPEx|^d*I{FNv&U$^@ijYOR_hh8XUvN~jL+=E zOtDa@F&268*li_2P^nf~`(4sy^1wdrh*MpQY9-%ciq=HEPIDRIIj{@2N$t-CzWheE z!$+PIc@bvS>c&%}7mY(qRmaJ-B$NZVPs6-t)jrO|dhx;+fH@$q}i$OIM4p6 zIy4n=O&`~DLU}`0R*ftbG^={NkSm3i9M__NZ=9O#=_b}z^KTqV-%Tv<_35`50!o>< z`%ZRFf6|&?)hGF+hi34SWU_@83X{9imgmbvTTaLnjyNb5kNc>;h2i>adi#u1)c|Mc;CB5oGhj z@AslRDGpuyq>y6o`Hv>V>-?EbP0qHo`}=F+Wj$q;8Rp zzrnWy{a8LS8d%M9&Chsr0er(fbdI&(G3NPA7LT_yY(9(F#3K-Wy@a_W+Ti`9f?s~Z zL2}-s7=Wx0MM*ne(xrtJr> zmeeD%>NjNh$w2gpH1m->|BVrKc=C9G=KPd~K2S>^t#=6T8wBx@`hDOx`+ecpNBtGf z{T0mkHAQ=iM!Kf=_V|vaJFuI9|2?N<`Jq}{c6>~)tVSGgGNo!GEkp;NAtYM|qu!5@I81AgB zrPQW&({uB06_sNIJ}l{{qCWhmqLcrws6T6ve?rWDq(c_AX8*MjQQdVy`r*>Dc4o~v zYt_lqr%Fyelm}FItmnixb4(~?x;`lgv-wlJgo{VJP%EHLjzYCZEzT?(n?)gLS>@Hp{dA`|INw>{sjlAo_Kj(4aqbkV93! zJWjO{BX*n-2rj&pdKE&w?AUD{J0J1j;Qc`YCwJr}V)RIYkgn<_`v^Vm;5a+B^w+=yW%S9V63~G}l|=Nvh8% zRWc)zF9nfqh8O#quLG(5ZZQF4kIG82#yJD*FHzU7kg>bP`?pVjyrPvql4K=>7&W*&aEDeVg2)uo+$527O(>e$ zZ9Nu=_d0}9f-KQ*8{3t9)+vf1CgWLHG;y&SqXLAit2A|v-Q!L=FjL7no+H*S~tUr@?2oy*I69zGt9Ou-Ki&32KqaDC{nr6B8hKAp` zt^$CuD{cD`2?-l0sUVie$u;;}=+?F;!)ac07dd0HW=vB@rk**8y;PwVy2${w+EeRb z*yY{Fm6aAz!RBjkd{Og)pjtG<4wdTLs2lY&1D+VTqZUv<*+vGSa_Vg$Sk%_Z`BERS z`?^qa2j%FwEw|9QCLz%te*}A`bF=TwRz_~KLzvuk6?@?at3n#x6$bxmB1i3$K+zqd zK-C_wK-C_?tT&DhWU-?SYN76oaABs>nK!qpPtAnuFHH_~y3Y>0zxsPpqOobA&tl`| z)H(68^eP%uaM`{a5KS$)TcK+1Gln)A0n=f37M-VuU^z*?i$Jj8wF6G}Nsp&tu68p5 zFsuDwz!}9NspkNC&CST%RNi#jr<#-)ojm$8le(i69qn9E+xb76chWU0-8OsQC`c-2 zcy^A+xy%#jolukpM^~E%b<8Z!zU|jAZKv*_y}bgeFB(deTd`zw+j8en6({3Fifbk5 zx;jlwQ7;-euhQm(Si)${PQM14t{U9RWEI9bR=)(wMK3!>NNWG8@coi(IbU2oeK2rs zm-J-)-COZRwW`%^gHFR`+cP01q~@rgrbBEUSGIC1G$SnBK~NpeEb*j`$GwiV z>{RGEB0pNw8sR`O6FcBPbFvUM7pipTA^ex~IL|L1Z~^(DKq&Ha+6;s`?Iw zjIfA^j)H^&-X47`9zPN&gjc%1SRGK|8cde)2s>8K*aD_O1lggc1<7Fs+dkv|B|lIOpJ@UrLVx@8%!=zie5% zM#FWvxah2$pvQAt7R1pb995UhNXLRA%noGwkqL1ID12M1c}4VYDvTjw^SHPoUiQeO zngk@3Of|XCzz>)+&*W1GZ)dRfIWQB93skC7IjYW)p0pHk9@-r2RTp{R zFIwq@iq#mA>@y{&=6nM?b4>U7P*5i&uf5X*iO`Jbo+*OIe>In>gpJ{ao2P+m;y(e$ z`x?~2Z>434)RKo)7|IIz+X=0cR?()YmK)cOnNZE##adtgoKVY(;@re zL5U2-(SPB85=L%7qNf>{YZhZR?ILs+1)rQK_T8J!6g_s(6_~i=R&v4Ulog?@x}2FR zrHgW9PH$@R2>@B~{DsjZV&}C=+^8+;6jpBY$iJsbdzC@t2{Y{)hIG&B=pDp$h*f#l zRllmPs_axA)~u2xAU;{cK|D9jQL8A=8e%J}S&GqBT+HwS`#UE`5o`?fI9X@NUeyf6 z1tX?>hWq(%p`RMghE={lqq0=Y|B$llYG7?)O!q@yxBVfpn;8GI;#t@^Dw{a{U#Ieq z48UD^8H2~Ig)M_S6&NsBG@6<*gIKta5VV-^Vu_egfE>L!=?DQ=+Vo%wIJ`ym2Nt(% zyMkJU)hw!|B|rjTf%3A_veKfNW@%Gdys>hfMLzPd>%k-qpD3|;1NGAFcCzEg^WFaa zXWZ?^?^mBR3#mWdUKGI8unTApvOm@;%AT_}-C&x@55UI;;MGCr*QP^IKrLn)!!M;C zD2F6yHn6r!RF6)TP@MZW77d@QE+3uHO{n=FX^8__55XWC%oa+YyEm1;-Jz|=CdD8v z#x;Yz-LF4wa-rLdkH`gRUeK2?`jFRX4)4(3Wd8U)RD(bO+~J>$pYdgW>Y-x73x&Si zsC|d-g1+y$c)MxiWG1fx;vf0Ye2llszZ&n1Fmh9OeF+EUU_Pe$m>`KZs@ywMB%Ns@ zn~Dx1XhY6BQr*3#LA`>uGo!`0lw7$}t2ELiSH>nXhdH#SMF z8UP<*ZRsqUlW6YLrz|BjswXeiwWPRn%~il*%}2a7j7JG6q6i~b*XsWdYwzHkY21E! zc5K@n+jhscZQD+|W2fUhvF$vuJGO1x_GET;r{h1VT*}S$$9%CA^FdLbxFy!dl3if35-B{&5UFo+iGDduHc`sO^G;vMk-+; zJZg;_g@3M~G^)VqtkG%n)Z6QsiWG*<=CixGvkXjZHnEYFAQ1j8nvtYS82(Y;lP?Qm zJloSy2o~ZbMVrhlN((=fqBSx}1t!QJ_k0+cdfWz%!ya^{V@L@c%2!6UQ`tJkm*}G& z5$0na>Y2g_<@8up>QrBw1cg1U%p!^tr;gSR-+dYwH6us2hMIEs4N49dZ?irn%{YIQ zWGje99e3wVDOlUGrp%m==5E7)E#O#|;$3K2j1+IsQrF~m+_0$SF`OXeKQ9+C8vXq* zb+ACsZk0oW7b^@>S9j`tEzF~oBLwY^HEX?Ev9NQBi@``mlfCxpA9+k!Fu3pWrVpyN z*e0%~IYC(=p~CE*>#O9tJb`XwkOrfb=bxe}z!CgBp66x{93PgJ!m0<2jzi;NVCJd$ zzLLVnsj%-Ig-SelRNwRz1hC%Jj?BwmwxUj(z2B6X8H*3~U6|ZDOW4fcpWE>XR^l&F zHVG2@ATJvIB8iS?^EtLGmmH78%W}Yk9WU)WYF*L%9Yp&myE|)ZHBP~@@ zw5=Ztj4|VxTwkda7nTqGa9{7)q9g}_Z@oT+exOG79k(W}1(}Hb2Cu_(SFOX`7-3cR z9TQ3xNNT=IqieVAL7_i{M@EO%JtU15&_lH11W$cHvDu#k|Gv_1aIJwB(6=S`E@JwD z_NAQmv6Yatv`Psq7DqWLEFUm+h=Uq5k&jWHNUegCZ)0-7J0d{I&qhA~a^ zZJK7dSn(}QP8s|Gr`jJ_WU4G??u9Xx8)qxmQ38AVEz6R{6bnan!nkBcomtCLVHJRN zT7<%`wEAkgC#6R}Pr=NsEc}=o@`B@_T#O=!N(?$}q$=}UY==#cY5}%0J1&%*8cDA~ ztP95Br^5&6loNuDaYNPN`JtQcKbNY2-ufW#FBr+wiMT~bt+cf=*%4Yy+s4W$9#SP` zjflSQgpiH9QM+(#ItP#=8Va2A8+KVan8|TOb>B$RO-NQ^{6H8`$S%@qQb4xrD(C`^ zgH`X9xD-;S?jHlEj(|Vc}Peg(_fcDU#vHzFCNU88m-eMC#i%3{B za*yJCmk+ObQinUY@XC{}-9p_=je9;gU1NbWa?xSWeYp&3oI*5bbodkaM-&VGC}w8o zqnas669<3v!?x0~sh<|#CK{-7^4q%9^}ui7qgz+;jol&yr$TcLHpc5W^@iO+bF47= z4!o|3Mmw@9;ahCWg)HA-sOwmwtoH2n%lZDTXbCx89Z$(40oQS)2~M?S^PfU^A&GRB zgvWvP!^2=?kLS{Hp%aXzlvcnR2_U!J?VZXlyEy0N>78JPn6I5P;RK7YR6eo0 zQkDGE8V9*9Ek^) zdo$OULx~Y14s>EKASWE~V^0uC3omqH_d)x#?Ko{E$6Q2=!j67AuY`&@%GH$$1N?bJNpZ?{}_rMjC{ z5lwM{!bA(B4iSr0@G>LdSxpsJ4h-c%qp-P1{7{(@4j{8)t(VHkvs2jXN6!R@*Gxoo zPL{#-7+MucGQD2eH_h)-&RAExNb9KOF>$UcNphE84#o8%`~ok&6nQCEMsnG?7=UTq zRBmHc3YBW?Dp%gpmvuKX^VrHR?+ZtoQQvkR^kf~}ftCHIKHi@^-d|!hp%%QoFuueN zeAm7>LS1#I*lEV(aGLMQtTw^)UH7Pe+z?sLF7iulvl)He41Nfo6%hoX^>R6(Lhtp1 zCUxDcY_8)XfZf#1RK(9khVf~+C6-+Y%^&?pn?*LxmnWMnc4m>>;>NLcoVrc&d=?o% zlr3d2ViVTYK0}DO^S;4ElKDv1lX&io*|fD_u+Np?te06g&+YDT3N%r3yZ)`UE z;5f0vCrDz-Xg`>c0asXz-rkukYYB%&>F(3&?(3J4*3erPs($8&zf2Ipw$D}R9GGt1 z$4q?Gnke}}o%8^1Vibz`Quz8?R54o)=oQW7qjO!d{Y- zUYc+ENb>rm9lQa){CI@rtQs1!Yb?xMt)`ZEvwb3NqonMWiBNQfC+`*eXzK4lKh+0^ zeeqLSc2r*IHCaks2nBjMyJNMHvjxf-nH&Ls#@xyrI2=Ph*NhEmzlM|i)w?CCjS<7E zOU%nYBa$DYEfzljt_Gv;rum0K2n%Y>PXt`}VK8l=MEjE;>u*d|zThS|RCd16UjBSx z1r+8VS>XyZ%0}7Fi}DSNJiR+$b^a}e8;Klq)9?dpt%>(8E#hqUhuj0sIYqT#c(h}W zyXbX>O^$PAYujhyXj|4rmm8A~dvj{plMVXLA={rV+{#h&qaOXKt9xH;7M(L#pk*5!VRYkWf zn^DZI(xuU-7-7p|0l=^(YAg1_y+U>D9!!uk@(pWq(7DoMO?5qM_%4L(ddSZv2|LV( zWv-o%JzX?BO}>0=8TfHKr>KLQjvxm`v9XI2AAEW7r*Nd!8d2>nU;ZQ`@_+pe zT&+Affb?Y*tj={I1{0>BHH`+TuAJ_sV-IdSGpnqB{Sz|Z*>R9zZ1bFA5}LwHR?>^3 zwmM_yG3$ChPCex=0$~?L%pB{Lol~cmo_koXzt-5-2m8oS&PMdUi~})aBARCy6FzQ8 zoLc|>-EMweNPJQxcNBfHW^5o39lGb9yB$&lkme9Qg>*;Ow)gCB9!H6iLAhtwqLm#d z#&?LK&v7mw2w$FHul_lUiKnJ$F|}C}C?Ye|@6NiI#B6Kk0%YUlJ`EFL^}7iMq~5EU zVgQqTpDgNAuG866QqaapjKXzsrY|wbf?DMAvT^}~`@!5+xT>zvqExEkk`QyY8!b5Z z7`L%m#jAfW%)N+F#-yo_wVD(_y0<3q>059lTGVV8b3pBuX`ijmTIes^th&a|kV{>G ze;%3wMx60(l|z2|79P6-jd^>kYc*Z@g0gtB2N4O{uPY;A2(Wgpon7~*i~7V$5RsS( z_>D7$$V27A7&$<&SF0(XDhqx~VO$-j{WBxb2#)dysnQ#h(I&XsNeJ|#Z6hMM+AEK= z;SuNw;}Q5bL{klioWs=pB|%K1N$WDsyiw`I!rr@Pgr?)vS!>3ZU0+WF&>~P+Ooxtv zeG#@q(3wnY0ip2n?}co33?kA4R?)5eSZr97e<4XrcEMGg9*2B<_G(R4D}ilFq;_i+ z(wbzgTkc`tRM4VTJh7NgPq+|~k6}$f(nCD3wp8ASESPEa9HLXAJwjPayRLte+K*U7 zJallPeJ(LFsiu5fi8M@`jXiR%uA{vvqn#rKXR%E~t0L=6>^p6xgx54Qs54Ff_5{^~ zK-3+ChH-c>$S4Z==oqhjDKAe-OmYaMA7D^zM&;4z%LF_en&XWVXYT zyn_M ztpW3tJ|xTVsEZUy6dfr_n&Wj}M_!gOD#VyQ3)6dUr2rvFgM=B4Nvs0o@_=+l6_^8U}#aiGZ; z$bTxbI7ta~{kJ0be=G8Tsn_U#D6*8jshOvlsr-Nc&tj_lePH+xQPwzdKsUqpH?LVS zSp{vJFB;B+-mn{U9yZe9rYownMOszPE#=xEXi_O=H8XE&+NP)Qe~1z{em9IJTg8^5 zQYw?Y8F|SCIy_Jd!cXJ9 z2c634x9cR$_lkzyV~B6#sJ)Ij;N>o!nS~MOLK8Z$;U)BH4f@@bCgesO%b7dG9M};l zjQ=nk!7DN#h9L&AlV70_O|zUl#=8fQa+#&`j8(gp8+v3h=}M)>oop!_adk3#zI07p zZj4{}P6Cw}%Gt~2%(L#-v2f4bd0{*d7&P--NI|S=pk)$A7;&S8B3rCNm`DrFNyFa? zurlSn72BH)h+EbpOmV{cbp;NpvAc1_>M+@DV6~B46x+Rqp`Ys|pD6j4grW!YWCrHt zQdq2tD_HsVJ3>96L{XkO%j-9Xx-Ox=z4ZMRv}Tc&P@ffj($lkLO>8s6WSM0zm?kAS zvxtTo7%f^^5bI+kdg3mYOPz};8_hyI4FTnyZ&oSl^y08rQv?Ih+;GmL)B2z1La z>{1ywgc{R`WaHTc5Q8a$(?v=$#d?xZHGD2i@iOxy2SIc2#VdSsOw3O~rE z8(3t7-xSJ_IlXNt+-KHO**1DTyZtAHFX~7&iA{D_4nZYN-ASxZt)pny4&iEB4`QNX zvGAz9%LW_pr`P{x@tSZ`!}0j*fEOBG(Ji31rEbqU6t@Pp^|~f@@P#wr2kRyi&Nj{fZ8uMffsX?M29A#7jv7$ zq)O}a?S}Yv(7+Cmra}i1sjujs)-~u!l76$av0=J-*Eza;&Z1!SoHJ?=OA+Wd#h{P* zEqOmiV*0B>I<`EVzqCeZB_P_hF8U_omsS$568-*~=y>sAVMW+gvP_KP#ADT7hak#u z7!Rh1>=VR>r>!e|wm<7h1>LUd7H%znfYM*?n$=wf@}Vz3g^m z81a>VcLNDe;g@6hhMXC1FomW6@E=IOJk-%#>nm{6B|B8a`(;DbBB+Zc&%m^wnAdg< z>SsfSLo89Tni=gIG>m|c>HB^B6Lizd1OmxO(Xl&&+7>NYUfa23C<>$=5ErlF76t?) zNV1|QNiZ{8G1cwYzs}SD9feH8Um!@=gq@K7CFh`2io(CSqFC${Jw@M}o+9!e-eqGj z3^%B>;2xXo9rBt?BoX*WB`}CBurvRLj@h-n{g&Wspt9Iph>p@RynL^6DKJPcyeYpM z@Pp=sbLVFRt@I^LN4R=_OLH3eMX(dCfT{nv(S5VaDglYaUsU|SPC$xy=??S>_&)31 zGX8-9Z~haq(j{79cw{OD1_4cM*)iG9mSl3X%~hWIz)iQJhe6WsT(HYg>+Sw*h4ia3 zMzVw`DZQyee{ak)!eI9yk!cDtApL}kBqZ_P`_aSzlSBU4Hk*QDnnMp;vaV5}LD86s z>y;f&Ihz{C?N`Xg><#9uIZpV`z6PI}jlQw<7-B~xwNX95Y0WU7z{~$uh#F%niCcpH zgST*{IVi)?Is4g-89`Hy(N?!E}W8<%Txc)#jOKI*!lG`CnlsQX>Vhm{4Qe-44Nz16y@wxQGAB}d@ z3tP4HC?E6<12bFBI^rh|A@s?wysIh9=yWEeEb`?fX%u^;*z{=k8~xCOr*q%{LmBp& zVKRbje4%7sqeyyo6t-;$KTb7?Vk?!330FIFOIDQ(t<~{Z!}@tER*7~O($X3Gn6)em z8MeS=Wg?+QHp{uv3Ez!ssf;n8R;~~Iym>r*6sabo3{b0lg2awt$wMP)ghi`*0#--8 z{GQg%D%P;V_@hDBY;3^@I9AT0MQ;PtvKecGb|3Z5YR{A9kdC6yAXzT>CTg*-+AbEW zQ3AzGqih#SY-G%IL|4t)>6gTimsXrs@+RbU++oBY24`CSiR(B@<;7dP2pYbt&O8xf{U9G_JRMM$?6Cgqe} zq>G8i=)(_zvm&n@3|W}E{F&FP4txZ2_KARXApb(fsg&D>d4C#~ZjQ8z4sZO8=~RXe z{=8Q|vS#9GG#m{FWx`EUSJ2zu$wl1D3|@3E;sso!J{gt6)F6+&Rm_8T&lnO}LY7X09rfqjoxkH{ zwsDA_YxXRrOhqIkPYo@>*~MeTR&FQ8U8G>lLPpU5+4B-fxal6C(km;kJW2o(%N)9~ zK&Lmh->kPzvRjC;KRMptK>Frnc=^y)Y+8+bnpW-U9&Q4wK1HBDH^A#r zSirmKB*WmY7~qB_IK8M!0$?uzX!UoX5Y#u;P-YB|(Rts72QEynHdwI(;~}tQ8Ki41 zdTEwVrF6+u2fW*qPehy6^bU2<07nl}s-#XEE;<@CR*wL^d08P7CG^7sSW-LSGx<0U zcvNJ=3wv2M;gXwfaT|}g;{#k3x-1jsY64qIW*o0;Vd$<&6)3cuXC3`7i|tMwEAW~V zDYMi?aFG=c++yR#IlC-fh{RCL4NO$;2ULJ>{-^;tE&c z%uIS(l`=^BU0Hd&)t|bD8@W8gwvtQ(wu>pV;fY8V_VYV1!8b1JhZ!be#&YYGC1vCl zYuI2Lq-nnK1Azx(nV*45^JyZPu?@$w5SGFT`)X^1Q`YYB5|bdDT3R`h7WGZ%X5y)H z>4OWhGWDIjn{%YgYME=D=nIf-D(M)(_ZGAut7Tob6nPOsOndhFvl)ix*@DjfOdN{| zZ1JIG?ev`cbVHfqtcYsfCFXJ?rhJ_<=jUV-xi(0te?J?^c=y8gvZ^IF8m9ACm8G5_ z&L@>6{s=G!DH5O3Vdn{EpY-Ig zIBURZ?1TYsiYnWge3zxIe5REIR%u}daa`hxdPD)PXkP2SvI;)G_5tytAh#^diKK&W z$+8U7tkvWvCpYqY_YtZ*1$oTzo!~m3a46aI4L?bf9#sw6NV^2`6vbZ}`+O0|I&v5q z4;`;5){kxyIF;8-fV&T^(8N zovt}B5=2K+bns7lg91-T%{AIi31pWeWdN>^GbrXqF+vuCs{OW{HnS^302Is}6`lta zbAf?S*sL%8!7&*bJXbYAI4~F9`~Czyxg~N|zs}Awwkl&8`P4H+kA9yCS8ggP9c9FZ zywo!L*;v!yB>qq&ecvm%=aA|vmunQ?`QEVP{vy)QSG%$gKfw84^qF`O=Zc^b)kzc2 z{@yU4mvH;kwicAZ%%R2ks8Q=g{MrE`)K-7gQpoI`mEWQ-TLq9eOoZ>X(a4?C}R(yPB7JkKcp31?{vdO|4qFUo>$qIn0SJsU~h85zm z_o(Hp2%~Ic=dXijys8UMw3vc-E{G%+(H$qZs|(VOgV)}{I#hG{a#rn)neBu<`}f+K zdiy}b(_Hkdz?Gc2vX&ZSBUDKA-lB6=J4(>?+T8_>KtBIw1=NGuvyOWi5Y1D4Ah`ca zjt=oK|L;79(b;qDeou95AQ2cCH-XU#%j7 zGE)FaUyT=b-bb^@B>QkNc(>ou`DVR>-O+8$HEFQk9P^M3)8XjmB6SmtRYINrz1g4<6y66eJbMPmjsJGnk z{zQVATpL5Tg-?~B6fsM9A6s}K4^1L)_G=gEAQ;IQQhMbw?-{S z85R&~O64uSKMI~zZs^W`34)S)iM8j)C7hT*j8@%>(h>=2n$)PTIP~i9uh8 z!2elO01#3A)w&!30-M|*q{=KlhYD`VXe_CcB~ZAuzNn)Gr1Ktf`j0`PPljl@Sr+kd zcz-*69Pdabb1Jj{zWHd$CPnLrhXG4lj*G!BdlWB+Vk#q&;bvLE1MvN({zw~$$8{() z|Dp26d~QrXd0;E`+rxkKeNj^9(6mvW6%_&5h~+t>BZ zbkLn>thzn}7m?uy;*?X=GKD~UnMQfpLV2m4?hX@RqozwRPw>oJ!d71DEGHw^c z0dJ_E&Bd9B4kef5e3PAk*`# z6%`nc`8yo-*9TMhZ688A)b#S=G&Wbr3-5#8oFaQbm}R#NaI1jvoi^yP+XS&I4)Xm+ z)Qt$_-ci{j5%cZ)awf@X;Elq}KNU+Lpbz^n+n8ML(5NHBXWht?vCMVSs3Ybn&4hpV zMWla^qhhF~@>&*7oGZ~C^EHcd8(*Y|TT>s}CkpZb6N^DV!NL$N2>V*b`a(nW!{*Un z#P2;ewdV@Qv=W5+WM&LUGt z<5-7iBqkNa??oMhXpCZ4rqnHy?bV8v*QyNei35Hk0>X7nQD042E|aKRbgUhT-W-yx zJTA1si@va2n!j`P%7mn@&pjiB#mKJ=w zkB*UQOS;&{7T#Hk5zAi=#4idUtzMzLSlGRPol2R5(G;Pmu@JGJPbH1{22>T=*)T1F zDLf&L`@IK^?<##Q!Y$luga}GCfXE1fdLLiz2zbAHMaEi9jK5ODDVPi}M@MD%j&3BQ zJVH5=UvXgVK7|VFMk{Lt&eWsNncK;Gz{ULaN{Ab9Ca?TFk4lvY#X^JuAL_1$h zMSSf0oVbI0#IPkWs^SrEY`isc+eQgKlaV))tEhOchg7%_x$h)s(haLU&z~n}VJW@3XfRT=G@*qhO zMoKA}5#{w`oAW5qL&4gANqr6`MuW**LRKH1QNAN6IEuK9CtK9|bM~EuE{3WsFY6{Y zd?>r?bX2{xoLEj$!WtMVmNQzhB=%B`7)RgF;33W#IY_8p_Q19_Z0GBpi0GaO!JDA4 z)~SecMPfbw*i*aAZ`ALR#Vf%fr~h!h%nMkCqt(vTom;&|=YYE_(iQ;13Pf;(VqwyO zEhjVpg}TntSMVb42~j;6_wF;8BrXfCHsid#dL($_g$_-M`=N0a1WcgquV*;Xk12i!!wzog8z{7fPp!WND*N$7xcyG3TdVZzhq>5dOI<1>WEg6CE)v3<@rO!laW423hc{V=8&4xuEHfTk-~FwR=&X-Hlc*wTlMJ7LOB&haEM zoe=5V@|u;j9rn=s+s~6y;dX;|3g0%=!+#ie`}$=?m4Si^)a3UES>FMo$E>?OTqrE}HEdOxsF| z5Kep;OE>wN79|UUK1~tja4=2;w?iJaXg&ptPZ)>IoLPQ<@w*ooxuYXZs%}HFPjZf7 zhmY0H6t3(&tHAw*stvG1xyyIFk2{C^NuoiuO7gD356oH3F8Ry%)ei_RjxJ&g<_r9b z6qoJ@2QP{OCxvdR^9RoAMYUl+!R}(3d&1yX=Bh55{Rv>f0viHq z(^PkeaN*$?#gLD!B!STkunIN00W{4T0;N${({#nMNxH5V)&y>-dM}ed#bkL|+;;*y zCg9M^&YV2z#;zVE->>pEp}MSP-BRDJbxooB@m2R?pgZkeCg=8KNi>*Ej;p5n-XAw4 zI{RG$TrcVw@eDsT$G-q%TfeF-1q#_mfqA0SqA z!5c@1lu8$)Dux@aH(^~G>FKV9SyjVO(06G-SdCcGL5hO1mc>pWw+K&HXKqrY7G_iFwQNgI)*Ur`VT@m!lDniGDRAbDzq>qE zW!5s}j8?VgbqEH;e;8%CL}jlOPlC!>@Ig2-&Q?WA@ly7eDa~rvBHt9-P~FC;#8Bhh z15AG54G9B}2p3~d59C_@U=(`4U{VYTZfhSoJFsE35kzG=pxk$%?HpQCbsOUt#+sqvKX9P%>=6 zlCHE~^a#VvT)fA|1J~8herH>&J^VqzwP>!YWtV1Z*DGs7d#JsXzquMau!u<4 z;BMU57}eB?mr=<--;2<)*fw+R`#W=T|KuwOe_3;4Qvn_?`0{T3`?J3{0+irSRS`}h za%`9Q!m{Xd;JJ(3Ct2yUxm|Sb^PM6DNbkT|KT+bKu@SzHD}dVHDYu?jATOaG{w`+H zYn?W4nB$*^mOC|d`)TWzKN~ssqa99q{p3@q_|0oX^qtcGvek%M6J%la8Di@l^%dn% z;|uIR&tc2yVd2-`=P>T?8fvlsxob3p%2?aM#0w69Tzq$p#F zVu7FA>tND@+T|(!S=@D}wxd@51-O)NR%v2g1FT5jFqC$rJ)7nWV6(>~O93Me@*{3W zL~}k&;Dv5@8#;-BwkGvHhr0et-_iY}xB&lGh+lBk;L}X@lr||#Qcn0B3pQYW{VE~8}Sp4KTv5O zJ$hshhz}332g?8r?%bq~jkcUTs);s2Z8AE{quzCln{q-nq(=#utAdH6SeXD^uFh>Z zFUYH}F}cL{tj@P^6p@c4Dip&H9ViVq>e2_yXQD3GC2W&P-FK)>lTVo{jkPsZ*BC|{ znLk90c>UXtXpPwCa2r}^@T>mS7m16claj?QJCUs0`w?o@LI6rn%fdhU_5AnuS8Z}p z>jHJ({M(5}aQ-Xk1)4aB`8swYPGQ$o^DO)DcETyt= zGW5CjSO1QedF583T4T$x3ykc$>+G4TUNNKoHR8Nz$wAg?;U%@UlUzIUbc2PV{c2L`e-;yq!6^^5>y%QA zWTQr+!!b-h2V}<%LPxB_wF;O$WaE;J&t|ARH3ob<4SLNmG_L>jOfDohMqN3oRc(WY*I)@YQ*I+Z0ARQ@2ci%)s%Cu(Y(7sKRtHkHwoLx7}+ zYU8J`t>Huyf}6OR?oQ?}`WyyPC**MVmr|S_Wjq4K_F-y@ z8R@Tfc`0Q>_YEQm2G>B@tS4GBXC8xgYp6engknSiu-qoX?E!9a;ZCe=t>F;jb(b2s zJCX5U1J16}!CJS}X=@NWsKK5L_m(Tv0>(3jjZcQXv0l0kyIl6=Do3}2LM{Vx79~;K z>lx_Xyj`#5-+Mc06}D&kz@=&O)_p>)r2qyGcTR~QbgZ_RwkAckdpz_n_GdqKJo^Fx z+(??{oBnJ^y=PWciu^pSU_Z`k@czcP99SdL^DpZ8{M3=Z=ErZx zwu1U(fqvNi)XG_Vo`fY{VTgT$TKZl`*UTOQ2ehwn(c849*9czKDw9vcUdXYinF6EB zGTVXs3PwcQd4D~4SJU)~oi*~ZPEaTc5mgF;oYn&?xFit)fvGrq>e{P;)ULPv3fxOp z8ev#>fYxpN!)~x0ALz)p@wD7PQsG`D2tC*~sb|jJqJuUc&6@2ZkzGdH4#tXK%Ag*d zf!i5|3=Z~+1vy(_<7ZTN-g|V`gwdCeVfi{IV$FaCIl{q!NxEO}=z5U1;|z%}4t3eE zY`zH$8Lk^QxzdV8V0X0xE**g19pm2jDB1gg?NBK#c;W;6suAt$$o*;*&i{LYGw1I_ zmHsyZh6(l?1M^=WDavM!W=7xUivJ4*^S`<)vQnlr@YK+Ux*1^rOz?8DIfpn+;(rtm=`6!H|A^&SN>9=0njBf?WN`{HJ~n z6d}KRUGI-ii6uW~ms4hfJXU97Jj^=6lweFy2g%oyc7}pwZ*@@Z z?>=LL+dFcSt_3*L6PROgaI#t`NrR9~fqNpG;-(DHS_< zL92B2Y+{}aR*!`C*XnG>4Do@*31jY7yVOL8n0;X>#)MDopa5OQ(?{?iPFx+cYq@l< z7-FsptVUZi3zY`-dAT669WB-p+=}v|yH?u!6&-JY(R+6at4w;)t(CI|`@OI8$bc@> zLw;v|vfTtp@MNGl5esvFLEYkuh>rD3noj8Q$j+2v!OG&Mrftw44D3*C70PKdoe4jI zl9cu98c`x3EY0HV$#+OMdT(UW(TxyO9i2p^U zevR%7za(!n@{=(GgSJHOjL&O>ksF?L?XWEw`}P^;Q$iNruFcm*Xhq?}#TKr374x!CQVl?!gfscLXXHg(S2}9Cc!w6@C1h z#!^e4iR5o1LLoCs6~5Cyvr>&@qB*gn4Sab3m1jimt__5_mslz?Yg@yV)H#i38+A$D zLW#04{a-XF@1L{73MOrn^N$g!@uO%Db~ilBPi)uQa@J<2FA_x61v%o81)`Ub!3{#X z&IDu`i2lEi+Ka)ukvBcdm*7W!+h*&*#>{sa*9dhTL9+x)eL zc2s&3KxeS75jng=^w?AiQ~rR-I@`aIMd;-b7Mugs2U&oMSXJ_F8bTiADl!2jZ4tY$ zGj)>aap(TUKu3f?D&4}23$^Wl1pco#7n0f|5pIX?pO(GyedKs zOu6K#gs1NUBmenYfd7+v23Kbi`E*Xy4|B^EA0Q=8f`Wc*I z>3#-_{nH~!66+8{`US=)tBug%ipCgYQ+TlL)`R5@IoO8hOPzXUlm1dlEkWxatK2 zVHJ~n8nlR94o_Gb)Xu7rVFZ;Sbi>u7*UI2Nk8zSqfZNU0*O7<7s}_lel>Rt+FA?ce zjG#n)YU3bP`)4%k{$;Djl*eW3*2|@qf577$;}7}YP&1)P(Vsv0B-wK@z>K*z!dbD= z7ys6V4mhe0N+JtoqV>J}6pu&T1!V!zh2KT37m6mPG|?5$?$uykSj87nXfmZEOMOOV z)Dd@^ISjQYKeD6Mnu&%Oq6ARLG)$~nF`rSEjyyyTIR50VVJMUOr<$l`zj9BLYm-uy ze8(Z;+T1gO(#JHWt6>+T_z6<&@&?}*p(ivoM!_-gGX~o%CXal~jlQ{ssQ@76RxC|h zrBzU9_41l;I^`to?^u!*oo-%koUW@}(UD1a9ipu^#(k8Y8-x{Nro5=%sB12H z?k^voXbqd0QJ!nhXy0Unolcv6VV}*}BHuAsQ1=S3+gG2Q*5fn7kC9^qr;pT6&v0b1 zT20@#%O`jD6YO=)fZeJF#)m34tuf%MGwJxoe)U0TBt+g?++m3$!CQ0Iw?3E2n=| z-aMy2LpZ+fPe^T|#c)v?J>w-5^SvaA%cNb*LKw$8@sneUtMI3p?xH1+dG`m24 zM9|*p0quoPF9_c3iykxzU7!@6UbRxp6L8Z?T?+8ruaL#7)Zr7? zT!1#-o_)pXY))fVfjAo%lml(u%x;Nz8O0nk(mA?4GDN!81g2i1L{8cB{KwRPl;%!y zZ^+Z-c(Q*{ePYvnvhpnv_O*jjXKVhsf`W1n9#jtm@ng#thEqmwBVwGBZigGB4gV2K z&QWwJ8R1&(;1uf{oQnE|qHK)dk5p|dhRAaTITM~|hmt%I4y)g%M7ev$)kMgAG7dr{ zmejokf8c>R#bboNWW*2OhZo*v_z02J@@J9I6N*Pv+yt>7!6|ZL2UpAln+>5PpV~f*a61yW|#+g)B9412Tlz6NuG+7Q&XnOf)a_K+#Q_g zrP%A&DXREia@sJyL8TtEaYInt$QH5W4{VJan1|0PrkJU+k1f)v8tOs6byu!7A--s2 z8gcNqV?9hKvhV>NQ8z3rF$!0-HYa=#?U%onSHvLry%L^@$b_Rih?^wAiqo7)a&dhx zL2i=@vy6X2ORj4zj+30E#TnnwHmcpOsS!s!eZu*BcPOtvgkYFD_LAza9R5=qpZN@Z z+`ei3Lf>=_)&CFL=wW8&XlrHv|B^>C1$Q%NTO&uIZ(zs&M3}27{r48~&nEsBWkjlQ z*wS$2+D=dn3^Xuf)qJT$?Pzko>n2@JvzwL*4+)`yPdFl9j+-pe`Da7lM?tI1&I+Wz zY-LIN&TkXvcbSf1fUPYWCJ{H94Lv_W!|n--}*R z(<4u%qTand;a^Z92R+@LJ>f?0lM59nLzGzi(=29$lz)!aXH6RtM4#JqkX# z;et&>{J?^Hy-LSMhxZ>Mj{4_Qzk(u#OSzAS!Wt)TR~xiU-m)3?#K9OPTHz+&@gsVv z-+GhSFD$Y6(DM%*-}Nkc^%D0NIoX+sR8To4R9EWR?H_<>T=Ik@b7T=<1~E{$eij}O zLdtYUi(2d+yl-U6h0k4akt&K|9Q;=()-=|oU+y0%xc~XPV_eCz5lOK*`|MF1x#qiMNc4RACdb@qOXvwo^WVa`L$8ZZnTxcd;5JP zt`UlXd?ije8YEpXvZ%9;?9yB}cqTl>$2b)2HcD1YP69Ns=Ar)Z1w97;`OKHd?+4Ak zpZGP>k00XyKRomQL858Y&~icl-U7T+TxU>5`qQiN#3_(XR6``em$D5)`!IQPP znWq=_75bcCf;$>GZegijPhb)43ssQ)R@F*D643noIri^>*CUk75>>oM`tVV2lK<&# zlKUz1(%|#`6Y9s)4jdQ^KAR(Ds4F~PxN&JDYgnO|YWh(54OcdD8Ks*hBz*L+>*6nZ zXEpp)9>g(2NpOiGoVgwB(!Q2hQpj(-^dNgJel|3w=>;|54gnmRmf6Y>RssRP&xt_c z%u`%M?kefxs7ly}!WW39^rl@RwliF3XoMTjZZ}L*Fnr*oaC7 zg%Iix871Z$LCILTnT_AT7A3P;W!gI{lZk-75`#q}np}s|;O&?^Bect1I7}S*)>(k_e%&K3F0KSZ$VkPLC3v)WH@(m%0W5@wiY~Kg{szZ7F9u)M9m)KSou= zdy>yJoeU@G?n9z`2SpB`WF{&J8=JjjX=d>k>lB6>_@+k9FMevyt*(~u><8YD#@&AP z{falKnypMV2V;KnN%UXbya@NOH$Wcckx6dE3n!6^(T)dLU;BXoyuv# zNQp)S7Ip@og=9JcLeMsMM7hiX}hdI>nR7L;wq zJMrS_512P-L`c1ju9g^i)xYJOswTN_{9YqHsY%g>P0Z7SC7>37v(}%s&?weYg9SaZ z(kaqBY@wo!XOiOl%z$Hr)nKI5Jn&r_Z3x#b(wvYQu^Ek@%e~7a9O5rkbW0grWRBIK z;N?B4W3l49-m+s(+%xN*-`0A9R95_w(Pzh3!bFZC+aDO~jTR!~(H@JAab=>K+5~lJ z(aq{&+Pg6*pIREJ|FaoR$9MIn@{*$6`P-EF>vm(_0$iP|_qU*wJ#9jt%lih=YwDKa zB17)rXqnqCkMldZ68O#^s%9fPf;P!JZ{jctVmda)itq<@s$3z>u4;$P1`=EMBhum3iTw zT^Er9s#6!uUPg$NuU17(uNjB%`xFKi)7`&h-GGO-XfXHoO7UhQdKH$vRi%tM3Y46k zPK>X-r8RNZIe28^<`Awldb@{?O{B^#A6M_carO?*m51B5Xvel~+qTUWb!?j*bjP-B z+qP{x>DWofN$0J7_Brp}z3bgm=iaJSzgmC5oZmOboZ}nAWiO4*QQme0)9pi}DVZH{ zHecTjWdu2Qd0X9qQ>n1zm2b<{T=Gjb0dm3iIUiEs1Ak<TW{2rX_I?rgS@F_5+;) zW5`9ZmyOj9g@uu1D;IPuT$}b_>KgrL2%Hvfe{9)>Rr*N7lmtDI4qr6JN3|_a==Tld zfGR=G=r^>Cr)2{nnAbAUR@`LUcbUH^qBuppNOQeV^$b^M0kC;yVViQ+|i$*|UVWwt$d2&F0}X zJxcJBc}Lv4)AVMBq3YbxYP-9)m)SWJ0}@QVADdoV`b4#{*`oxRv?-cdqHb!wStwY1 zXaa15HUu~zO306im(k2C4MVnUHArmCBbJ7tmZN7WWUiAz2W_p;mLqopS?dKaf0PwH zM|mEQj|!LC;kGY_mlV$Sufo(U7+}n?lLovgjDO~dNHKjeU6VNR*oXUe@*F85NdKs!q{jH8GG-HQ6a;uHU-AFyR2ODzc3HLF`!~E+ z+92Rd30&@cIvJyU{;thnfQXt-AMdC&{zXB7t2XuWWG zE!`-Na6UOH&N{_2JwLzi>~4(`{rvnu@3ZpB8CKDq+~Zkl#O{N#(#6^vqz{iBIyZrd zW)~bM4YWa}zw4zIR|`vFKD643n@e$23t%vk8DM~k3ED)}g~!XWf)BKA zwpPbU8(~(z-y&|>a_o2R;FqEBC;5I%kjqsa*NW|~xyxE(w@|quitW?hxgsI z%QYK|+jy{{BbbRhoy&XpvDm4n+md>vvB$GLSE7xTI*_+^Je0~m>#)wKknW?V)?yQS zf=zk9q1Ziwg$*gIAGv^V(K1Q5y&1Vxs~w5R(s0eeV9|eX=WpUn++_s?kJU1{=ho%` zqovz|-wCMlBAC6~po0UIfCV&3KO&~*FL!a+xD|}e&vYo>J!DW@mrV~o^5Ve0-eoJ1 zO3j?V5huPnZIoz}T6iIA*%dsgv@wh@1?xK~EV_14Ru7|d;w%`EY?haxA!VT|k83Yp zyeSCUMY0gAm)s}JL0$EYoHM%Fl@3en zM_|zA)V6KiC&yTHk`#9a&IW0X8zD*}Vqn9Q;fXOveSYt0(CCOkEH8YxaLOJwger%=K-LId-RJ;BeXlH>AB}MUFMy&Gdf6C2vB<1 z%WsGhAxqED!qyRpH! ziGjXJEZU^;6UM-+AirYUT&NOq&go!ckkZpW>AvRstxb^9;+3AbE{ z)CS8+{6>XoO6we;GdxG@e!-%rHOx$-I2XJ2ZbKwrq6G0FdkQms zOLG;q`*!R>shC~l`zTnEr0YWpbE$+cH&_HfcyN-Lrbcx%BYy@bdo1c2twI`W&?ZWH z&Uj4fnyo55`ZU)|keYszmK79cWgsG_88;zkyC+G|eq|olyGMDTDk2ist5q^f|1HX1 zUitOyUkOEePr2_TFySs7_1ia&|2?5_v^4^{LR&Z+Ia)G_TRH!Sy_bZk#eYUGS8H7S zMAyXeGk0iA&nDlD3sRQLY?0+k3=-~xlj#>#CFX!hB9^`kmT_N`?^s`-Q6$Ze&}nX2 zZqd1baOl>Opwm%q5LVHt)~WWeTaML#LwZ@>ot<Hf&`1?Tu^%$~WAtURa*h&CCB{tmO7UV_GHUI0aPre}Ob zKvkv7XN7zPOED{*&bn^P`;dg$AKAIp zu#pWdq}w1D88tdI zEYI6-J+dD=&E$uhtU1ZZo%2)aGn_D^nm&(a?QgjdfnNAG+#6g7*a*Km-~$^i5=<|gzU@=Qm||mkn#)m z%G|funiCP&&NXDC?_TZHq%Qp?#a0sao5mvcy*f2}*oQ!4;#nv&%+%~SL9bBhUT&v3 z1ddRfJQvdJrnFpgxcFDngMAsv6f)k1>egpT<_@^90A@d)})sqm|9-`p$#=4gAg< zqZYcmwxaG9%8$s}GfdBzzz{>5N9=cx*L*ZcYfBf&cAaY*Yxg9SI}Co>ysc~_{d~rG znc(>mO*gYOzMHsq+jkckXRGzlN7o<0e*0SO`6D!>MtXK^QA(3e^iH@Qx{!M``uN%N zOM7{I{8-ue^Vu|aR&IZEYnl4ambwE}1v2Yx7t;&qI$KgLTjUc@pS4HVOfsiZt_5xc zIKI4JeolsCC-#ebig^mlgFam+(09mF*0c28_qgqBX=Oa$|CWaI`UnX#Voj36lX}KC zS}zI-;&TgL4-a6P6V>3*V-d*Za;7i5Ub??Hg6>6m2fHtB=#2UREaR7T5q534ZPm-> z*Ho}Nfaxhae|@Vt>NSb}ImD+D8kT1F;Z2BwHoeXD!Y7s935@rN5%4(aa`Bj9^*F&k z+!5CUrZqL&AD$TnAs_eblNJFv4(^|aWZq+s%KC7QtN(j)OXwj_h zlh|)iHI&boU4ze8H(?8!dx}^1=j>J>5i9M#f>xTp0c^tQ^Ykz13yeUbb)G>JHnhoS z4`A)1w^+Xy5EhP`u$#4v>3nZe5R3)DS*~pSZaMX8qR)GTR|wit{*muYU?JRT^|I}H z%~^GDVvKNYNP9iRi+`MW-j&^lO*zi7rR*VwX>Yvd>nReBF2hKXfbLw5a*ouQBrPA* zi!Pa-Kz&zPADSdTrN4C^%ES&$K#KV4x;~yZndOd9ne)p*Ye2B$$p4+1VQOS3aohsb za#Eu82dY`CiG46k;rrbTts;9aY?)I9uhTcE%3q27(8rYlyTM|BzDJSC(aM0)^v0t- z@wxkMKz}O$qEh767x*XDSI&@R6vJiiDv-{=1w9W3a;=DYznj;cTx(Elbu;(vz@)Lf1JlbU5~d{@Jlz!vE4 zWUhlxbyOSgfMJ<8BUz9B#U;uPQV+io<{1qxOBFZsHFm%BQ>1L(#+9NdZP=MOS%famdRm z8s*%2*yFW>kEXLMu9Al)cJoUdG9e@G6kR%#q3q_d`ONhC6E(S&gk**dFV*#6W9u1) z63lzaen;n+dge^rCVZEWCe_t88*X$|&Ia}Q#Iej@5EzI}!0r-b^#cbNv#i*xuO+(*z^tp2=(;T_K2zj-NWU+dLO1te zNGnpOV{U#tQC7c4p*4u7E+Yt-98sVJa8#OcEQ5Htx5aVP!nz8mYsjsest+6x^`azV z`rCBF=oO8C4J|vALbw0V>hKHk9Kc`FDtx!+kdt=FD!0i-xb(aU)gKr8G)jfFO7W=@A zxLa(X9dU{Dz!;^doMZbD7nRs22%kK8xXT3=W;5B)?cN}hHq`7y6(6-?igJRCj<(~9 zq_O2ucoC7l!ii$tkbRJm${=Dq^S5%3N)CMU#rIZ;wU~=CS(A#?cOLOW4R`?a6a|T0 zU;|>a-$-GuH3h6ey5be7R`cx5UM zH|#1>pkW=cW$$5^R>7WA-_=kWl_^v-RTvyw;UDM+g|MJIXOKFfOw|q z-=*$9s}49c3jnkJ+A4TbE7;20m>v7%aTvk6{EbI{oeI*NQOmhssqk*Sc=;-px!h7) zd8KKMbxQV~x@UE@eU7Lz_zhuO?Xw=iuK4`}gYKEbdru<|=1|aKYmwBm1Z7kJaJDe5 zmDno>%5*?6>=&*gz1w1X0-NzhVaAG?t|RjBBVoe(p3~_XTx-*3g%iV$;=Mcx&L8b- zFzFMi#9RFBZ+^Psj~%ct(0`>{w0~e+n83b$qlEtUjp2VrxBeHJrLL#6sEqB`k?eNC zD=Y}{@$JLVH8HyX19UztfE;=NIWQxwL4(@DpWm4MH(3vq&Bmleo2O#bCe`J1BpO?7t2?sWfhIZgEUw3F z6vV7{8uAvB!rVL^jKW#Xt^wBH+37}9ySuE`Y&Oip6?rkM3p8|AZ42@gGQPeTmP!9RDq5{ELMHK+!{@)U!zf-afZ#fvGH@|3MCRFhOj=m zVVRiBd_|+Z{9(jLcAQDetfWRKUL zx-FarW)arDei~kSRXQ-8c^ngGV$?d-aVs86 zrs$Uaz(=0UuUMWT6CO!t)6t{*Od5r9lrb!52z|)~rM#{UF&2@a;8xV0{RMRvJ-1Qd z7knEYg{(MFzX)3qEnR#@Ea$|OkEVxTg|SW82m47h*ffIkD?|2%%oTcZLTDOgieE%< zPdV_~4Jkrf;PmL$DT|U$+&vfRD@J<#vuop?U_aWiqK>2l&VaaN$+xL!eTbXv3|)bp z99M-m&QcsH7+!6yk1Rl!NM;eC9#LjhBd#!*ZbPs-yb@DU8e z-=3Ls3gjxVjunEXvi*pttH|r5qI%h;NQVKVJGX0%B2Pp8luxO@{_(91;iV|^XSsxA zH}yr(mUR~vb3m$2{SeIhU0ys@4vO(?N!C-s=!s@I6t)|d%5p%m^~jq}_W>jGA^axv zhPs{5h471~?bmWEtu(?K( z`8*iqb<^4$4?$7;PY2qbb{!85Nc=p~IOkG+*#pf|W_WgAv%POVJ|3?4evxch{qmoj zfR}Wlh(QiXYOk1T=Eijtkh}?DLc1*(wPVxmxc zgjGF|)t=UvRFTQ)@?hSt;Z#>%y!}zpH%G2nXo+Su-^X z#%wh0hBu`!E{I`H15TV+hd!TkMwYFc)vN}Rz$B}X{UxAsj%Ka0*!M|GuiW{ZTq8{XVMiY{x4T1*|P z`Jz-Uw|WmOTYlgDwW#X)u0D#K>$VDOb&*qzSmDO>`fDN5SbUUv=BzDR-@pH7j=`u( z)@r=+aoEC{WGo^68)M8N zVF$Cn`*n_yB3-Y?9yUk$SS{7J-3wFAet8MCQRNzcz+QpW*D-*jV*3m_DpTMeZ;&e;>FqF~A}iC_LHQ~E z_mqR=oQ_0)B}jTHq^vf`vl%IfP*}Hbm^QmdVTjI6|E93;FJ5t?xI?tj!374ZgEe9z zFZoa>81Wk-)LlNVDB9_X8+DqT-_yTK6<%06r4E4&cLLn-{12}>MF-RWoG4}k~41CXtG*5EN);zXiX<4=E&a#%X3+;5n*VE+i!(MEH)X#8Jdnfn(MMFnl0z(i?_Q z3R|qP4#paDKg+I-L`QB0*VgpBXFp`$I34x={ridZP0cfH1cJWE$dDyEv(3zq27zD_ zr^;}-FN0q@Rj_q&%vfM}o1ooxED-7UeZKC5^E!K-*bOn{>9K>@|S*p#38xgq#7-9^g}MR8?Cv zU>dd*Oe4;5Xzhs@{*F9P0NHNyCoga zy}rDe#W7-hc1}*c5ZZ-spTzPv>y(RmK*#@1c+x$P`SHi%brj|BC-}uwHly6M1tg8` zfE+Q*ZjTr~m=7i#)tn;oJI3Ag=-l&&B1;{>cHR8(Zt~4Rdg#{hgG%C^b=3$Wo8kyx zY{~>2Zp;Lwc8-@|0cdRxS+;$#b$2j-f$-N@T(@AVmD(_9rM3N>zc;{x)XK&u_Kru3 zS}>&EBjPuWs!(e{>9P?HB`2V7e-7tH5(EmclTm;lP5pd|cH&az7~z`#0-ZjvMwVu` zKyMOoSZfk$))DKn2KY@zQY9gX9zVG)fstWnacz{A01F3?4%{~A)daEnRX|VAk&yld z%K2gY<^H6k7eRBb`}bcbJ;#d!XCCTu09X@j&3dY&5PJsm)|5cD=37$9JsT8mntm$o zXA`WQvp%Zto;&-{y>rQv%3LCiQiO|bHKMzx+9?m)X0H*jJ$}~O!X4>h_Bzc`tgyJH zd6^e@8i$)PTd37rJA>~igU;%iBJ00}j827wV<$fj2wr`6Mec7bXDWwGzyZNrID?#L5%=Yqkea#!Ktz-MeqinQK$22fKng_|i{0u$zilpya<9)Uv zk*9+-^X_kdQVQE&*%9tj2}bSGIG-6)VwUc(F)7s~gfclXEXo=GccQs)xtQ)fJ_ z=rSFcop2ud*qK!R}58Lkr~b1hLWX~NO4;0Qf0n9oI+s|t%) zq>;C0OPqH zj2iVXF{^pVXAmt~*Gm}IsKVC0SL?}ripBlT?EW^4C%Y?%!)mL4&>lh_D?GAU8l+!F znZB{QK*s*JMgqen6!Q$_OyV-K0WpK-zeN99xF)m1C^bMuUI_BHZ|wiG!d3o{W!itB z%l`zG8dfgoX2^a!)7LZRtRy7(3B>!*{Ss?qo%zQDL%BDm!?4-2(K(TDW_G z+;T8Isp>2Qfgf#=MBacYiL7FBxp(!gXzvI|royez_t(2$0Z^|4Vm|vuBfde!MFCQP zDal`VL~3=pTtpVjaRF55_{g`|{eonS$_wpsL`$FJK{)r%*x{HU26Iu>jY!GT?3S)) zV4w)u{+4S(mWUbsp6ueSh4F1aHA%0DpwCbQR;`Tbxya0oi-koLt~5YA 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

    HEAXHN!rb)nu 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 x(aH;0$Y2cqb1le + * Simple Features Specification for SQL. + * See {@link WKTReader} for a formal specification of the format syntax. + *

    + * The WKTWriter outputs coordinates rounded to the precision + * model. Only the maximum number of decimal places + * necessary to represent the ordinates to the required precision will be + * output. + *

    + * The SFS WKT spec does not define a special tag for {@link LinearRing}s. + * Under the spec, rings are output as LINESTRINGs. + * In order to allow precisely specifying constructed geometries, + * JTS also supports a non-standard LINEARRING tag which is used + * to output LinearRings. + * + * @version 1.7 + * @see WKTReader + */ +public class SVGWriter { + + private static final int INDENT = 2; + + /** + * Creates the DecimalFormat used to write doubles + * with a sufficient number of decimal places. + * + * @param precisionModel + * the PrecisionModel used to determine + * the number of decimal places to write. + * @return a DecimalFormat that write double + * s without scientific notation. + */ + private static DecimalFormat createFormatter(PrecisionModel precisionModel) { + // the default number of decimal places is 16, which is sufficient + // to accomodate the maximum precision of a double. + int decimalPlaces = precisionModel.getMaximumSignificantDigits(); + // specify decimal separator explicitly to avoid problems in other locales + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + symbols.setDecimalSeparator('.'); + String fmtString = "0" + (decimalPlaces > 0 ? "." : "") + stringOfChar('#', decimalPlaces); + return new DecimalFormat(fmtString, symbols); + } + + /** + * Returns a String of repeated characters. + * + * @param ch + * the character to repeat + * @param count + * the number of times to repeat the character + * @return a String of characters + */ + public static String stringOfChar(char ch, int count) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < count; i++) { + buf.append(ch); + } + return buf.toString(); + } + + private int outputDimension = 2; + private DecimalFormat formatter; + private boolean isFormatted = false; + private boolean useFormatting = false; + private int level = 0; + private int coordsPerLine = -1; + private String indentTabStr = " "; + + /** + * Creates a new SVGWriter with default settings + */ + public SVGWriter() {} + + /** + * Converts a Geometry to its Well-known Text representation. + * + * @param geometry + * a Geometry to process + * @return a string (see the OpenGIS Simple + * Features Specification) + */ + public String write(Geometry geometry) { + Writer sw = new StringWriter(); + try { + writeFormatted(geometry, isFormatted, sw); + } catch (IOException ex) { + Assert.shouldNeverReachHere(); + } + return sw.toString(); + } + + /** + * Converts a Geometry to its Well-known Text representation. + * + * @param geometry + * a Geometry to process + */ + public void write(Geometry geometry, Writer writer) throws IOException { + writeFormatted(geometry, false, writer); + } + + /** + * Same as write, but with newlines and spaces to make the + * well-known text more readable. + * + * @param geometry + * a Geometry to process + * @return a string (see the OpenGIS Simple + * Features Specification), with newlines and spaces + */ + public String writeFormatted(Geometry geometry) { + Writer sw = new StringWriter(); + try { + writeFormatted(geometry, true, sw); + } catch (IOException ex) { + Assert.shouldNeverReachHere(); + } + return sw.toString(); + } + + /** + * Same as write, but with newlines and spaces to make the + * well-known text more readable. + * + * @param geometry + * a Geometry to process + */ + public void writeFormatted(Geometry geometry, Writer writer) throws IOException { + writeFormatted(geometry, true, writer); + } + + /** + * Converts a Geometry to its Well-known Text representation. + * + * @param geometry + * a Geometry to process + */ + private void writeFormatted(Geometry geometry, boolean useFormatting, Writer writer) throws IOException { + this.useFormatting = useFormatting; + formatter = createFormatter(geometry.getPrecisionModel()); + // writer.write("\n"); + appendGeometryTaggedText(geometry, 0, writer); + // writer.write("\n"); + } + + /** + * Converts a Geometry to <Geometry Tagged Text> format, + * then appends it to the writer. + * + * @param geometry + * the Geometry to process + * @param writer + * the output writer to append to + */ + private void appendGeometryTaggedText(Geometry geometry, int level, Writer writer) throws IOException { + indent(level, writer); + + if (geometry instanceof Point) { + Point point = (Point) geometry; + appendPointTaggedText(point.getCoordinate(), level, writer, point.getPrecisionModel()); + } else if (geometry instanceof LinearRing) { + appendLinearRingTaggedText((LinearRing) geometry, level, writer); + } else if (geometry instanceof LineString) { + appendLineStringTaggedText((LineString) geometry, level, writer); + } else if (geometry instanceof Polygon) { + appendPolygon((Polygon) geometry, level, writer); + } else if (geometry instanceof MultiPoint) { + appendMultiPointTaggedText((MultiPoint) geometry, level, writer); + } else if (geometry instanceof MultiLineString) { + appendMultiLineStringTaggedText((MultiLineString) geometry, level, writer); + } else if (geometry instanceof MultiPolygon) { + appendMultiPolygonTaggedText((MultiPolygon) geometry, level, writer); + } else if (geometry instanceof GeometryCollection) { + appendGeometryCollectionTaggedText((GeometryCollection) geometry, level, writer); + } else { + Assert.shouldNeverReachHere("Unsupported Geometry implementation:" + geometry.getClass()); + } + + } + + /** + * Converts a Coordinate to <Point Tagged Text> format, + * then appends it to the writer. + * + * @param coordinate + * the Coordinate to process + * @param writer + * the output writer to append to + * @param precisionModel + * the PrecisionModel to use to convert + * from a precise coordinate to an external coordinate + */ + private void appendPointTaggedText(Coordinate coordinate, int level, Writer writer, PrecisionModel precisionModel) + throws IOException { + appendPoint(coordinate, level, writer, precisionModel); + } + + /** + * Converts a LineString to <LineString Tagged Text> + * format, then appends it to the writer. + * + * @param lineString + * the LineString to process + * @param writer + * the output writer to append to + */ + private void appendLineStringTaggedText(LineString lineString, int level, Writer writer) throws IOException { + appendLineString(lineString, level, false, writer); + } + + /** + * Converts a LinearRing to <LinearRing Tagged Text> + * format, then appends it to the writer. + * + * @param linearRing + * the LinearRing to process + * @param writer + * the output writer to append to + */ + private void appendLinearRingTaggedText(LinearRing linearRing, int level, Writer writer) throws IOException { + appendLineString(linearRing, level, false, writer); + } + + /** + * Converts a Polygon to <Polygon Tagged Text> format, + * then appends it to the writer. + * + * @param polygon + * the Polygon to process + * @param writer + * the output writer to append to + */ + private void appendPolygon(Polygon polygon, int level, Writer writer) throws IOException { + if (polygon.getNumInteriorRing() == 0) { + appendPolygonPolygon(polygon, level, false, writer); + } else { + appendPolygonPath(polygon, level, false, writer); + } + } + + /** + * Converts a MultiPoint to <MultiPoint Tagged Text> + * format, then appends it to the writer. + * + * @param multipoint + * the MultiPoint to process + * @param writer + * the output writer to append to + */ + private void appendMultiPointTaggedText(MultiPoint multipoint, int level, Writer writer) throws IOException { + appendMultiPointText(multipoint, level, writer); + } + + /** + * Converts a MultiLineString to <MultiLineString Tagged + * Text> format, then appends it to the writer. + * + * @param multiLineString + * the MultiLineString to process + * @param writer + * the output writer to append to + */ + private void appendMultiLineStringTaggedText(MultiLineString multiLineString, int level, Writer writer) + throws IOException { + appendMultiLineStringText(multiLineString, level, false, writer); + } + + /** + * Converts a MultiPolygon to <MultiPolygon Tagged Text> + * format, then appends it to the writer. + * + * @param multiPolygon + * the MultiPolygon to process + * @param writer + * the output writer to append to + */ + private void appendMultiPolygonTaggedText(MultiPolygon multiPolygon, int level, Writer writer) throws IOException { + appendMultiPolygonText(multiPolygon, level, writer); + } + + /** + * Converts a GeometryCollection to <GeometryCollection + * Tagged Text> format, then appends it to the writer. + * + * @param geometryCollection + * the GeometryCollection to process + * @param writer + * the output writer to append to + */ + private void appendGeometryCollectionTaggedText(GeometryCollection geometryCollection, int level, Writer writer) + throws IOException { + appendGeometryCollectionText(geometryCollection, level, writer); + } + + /** + * Converts a Coordinate to <Point Text> format, then + * appends it to the writer. + * + * @param coordinate + * the Coordinate to process + * @param writer + * the output writer to append to + * @param precisionModel + * the PrecisionModel to use to convert + * from a precise coordinate to an external coordinate + */ + private void appendPoint(Coordinate coordinate, int level, Writer writer, PrecisionModel precisionModel) + throws IOException { + writer.write("("); + appendCoordinate(coordinate, writer); + writer.write(")"); + } + + /** + * Appends the i'th coordinate from the sequence to the writer + * + * @param seq + * the CoordinateSequence to process + * @param i + * the index of the coordinate to write + * @param writer + * the output writer to append to + */ + private void appendCoordinate(CoordinateSequence seq, int i, Writer writer) throws IOException { + writer.write(writeNumber(seq.getX(i)) + "," + writeNumber(seq.getY(i))); + } + + /** + * Converts a Coordinate to <Point> format, + * then appends it to the writer. + * + * @param coordinate + * the Coordinate to process + * @param writer + * the output writer to append to + */ + private void appendCoordinate(Coordinate coordinate, Writer writer) throws IOException { + writer.write(writeNumber(coordinate.x) + " " + writeNumber(coordinate.y)); + if (outputDimension >= 3 && !Double.isNaN(coordinate.z)) { + writer.write(" "); + writer.write(writeNumber(coordinate.z)); + } + } + + /** + * Converts a double to a String, not in scientific + * notation. + * + * @param d + * the double to convert + * @return the double as a String, not in + * scientific notation + */ + private String writeNumber(double d) { + return formatter.format(d); + } + + /** + * Converts a LineString to <LineString Text> format, then + * appends it to the writer. + * + * @param lineString + * the LineString to process + * @param writer + * the output writer to append to + */ + private void appendSequencePath(CoordinateSequence seq, int level, boolean doIndent, Writer writer) + throws IOException { + if (seq.size() == 0) { + // writer.write("EMPTY"); + } else { + if (doIndent) { + indent(level, writer); + } + for (int i = 0; i < seq.size(); i++) { + writer.write(" " + ((i == 0) ? "M" : "L")); + if (i > 0) { + if (coordsPerLine > 0 && i % coordsPerLine == 0) { + indent(level + 1, writer); + } + } + appendCoordinate(seq, i, writer); + } + } + } + + private void appendSequencePoints(CoordinateSequence seq, int level, boolean doIndent, Writer writer) + throws IOException { + if (seq.size() == 0) { + // writer.write("EMPTY"); + } else { + if (doIndent) { + indent(level, writer); + } + for (int i = 0; i < seq.size(); i++) { + writer.write(" "); + if (i > 0) { + if (coordsPerLine > 0 && i % coordsPerLine == 0) { + indent(level + 1, writer); + } + } + appendCoordinate(seq, i, writer); + } + } + } + + /** + * Converts a LineString to <LineString Text> format, then + * appends it to the writer. + * + * @param lineString + * the LineString to process + * @param writer + * the output writer to append to + */ + private void appendLineString(LineString lineString, int level, boolean doIndent, Writer writer) + throws IOException { + if (doIndent) { + indent(level, writer); + } + writer.write("\n"); + } + + /** + * Converts a Polygon to <Polygon Text> format, then + * appends it to the writer. + * + * @param polygon + * the Polygon to process + * @param writer + * the output writer to append to + */ + private void appendPolygonPolygon(Polygon polygon, int level, boolean indentFirst, Writer writer) + throws IOException { + if (indentFirst) { + indent(level, writer); + } + writer.write("\n"); + } + + private void appendPolygonPath(Polygon polygon, int level, boolean indentFirst, Writer writer) throws IOException { + if (indentFirst) { + indent(level, writer); + } + writer.write("\n"); + } + + private void appendPathStart(boolean useFillRule, Writer writer) throws IOException { + String fillRule = useFillRule ? "fill-rule='evenodd' " : ""; + writer.write("\n"); + } + + /** + * Converts a MultiPoint to <MultiPoint Text> format, then + * appends it to the writer. + * + * @param multiPoint + * the MultiPoint to process + * @param writer + * the output writer to append to + */ + private void appendMultiPointText(MultiPoint multiPoint, int level, Writer writer) throws IOException { + if (multiPoint.isEmpty()) { + writer.write("EMPTY"); + } else { + writer.write("("); + for (int i = 0; i < multiPoint.getNumGeometries(); i++) { + if (i > 0) { + writer.write(", "); + indentCoords(i, level + 1, writer); + } + writer.write("("); + appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer); + writer.write(")"); + } + writer.write(")"); + } + } + + /** + * Converts a MultiLineString to <MultiLineString Text> + * format, then appends it to the writer. + * + * @param multiLineString + * the MultiLineString to process + * @param writer + * the output writer to append to + */ + private void appendMultiLineStringText(MultiLineString multiLineString, int level, boolean indentFirst, + Writer writer) throws IOException { + int level2 = level; + boolean doIndent = indentFirst; + for (int i = 0; i < multiLineString.getNumGeometries(); i++) { + if (i > 0) { + level2 = level + 1; + doIndent = true; + } + appendLineString((LineString) multiLineString.getGeometryN(i), level2, doIndent, writer); + } + } + + /** + * Converts a MultiPolygon to <MultiPolygon Text> format, + * then appends it to the writer. + * + * @param multiPolygon + * the MultiPolygon to process + * @param writer + * the output writer to append to + */ + private void appendMultiPolygonText(MultiPolygon multiPolygon, int level, Writer writer) throws IOException { + int level2 = level; + boolean doIndent = false; + for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { + if (i > 0) { + level2 = level + 1; + doIndent = true; + } + appendPolygon((Polygon) multiPolygon.getGeometryN(i), level2, writer); + } + } + + /** + * Converts a GeometryCollection to <GeometryCollectionText> + * format, then appends it to the writer. + * + * @param geometryCollection + * the GeometryCollection to process + * @param writer + * the output writer to append to + */ + private void appendGeometryCollectionText(GeometryCollection geometryCollection, int level, Writer writer) + throws IOException { + int level2 = level; + for (int i = 0; i < geometryCollection.getNumGeometries(); i++) { + if (i > 0) { + level2 = level + 1; + } + appendGeometryTaggedText(geometryCollection.getGeometryN(i), level2, writer); + } + } + + private void indentCoords(int coordIndex, int level, Writer writer) throws IOException { + if (coordsPerLine <= 0 || coordIndex % coordsPerLine != 0) { + return; + } + indent(level, writer); + } + + private void indent(int level, Writer writer) throws IOException { + if (!useFormatting || level <= 0) { + return; + } + writer.write("\n"); + for (int i = 0; i < level; i++) { + writer.write(indentTabStr); + } + } + +} From faea898f30f8b58d49ce97c7edc1425beb679c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 16 Jan 2018 15:17:41 +0100 Subject: [PATCH 006/156] add new SymmetricPower with extended limitation system this is based on jts polygon calculations --- .../core/utilities/power/PowerException.java | 25 ++ .../core/utilities/power/SymmetricPower.java | 397 ++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 edge/src/io/openems/core/utilities/power/PowerException.java create mode 100644 edge/src/io/openems/core/utilities/power/SymmetricPower.java diff --git a/edge/src/io/openems/core/utilities/power/PowerException.java b/edge/src/io/openems/core/utilities/power/PowerException.java new file mode 100644 index 00000000000..077edd8e141 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/PowerException.java @@ -0,0 +1,25 @@ +package io.openems.core.utilities.power; + +public class PowerException extends Exception { + + public PowerException() { + super(); + } + + public PowerException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { + super(arg0, arg1, arg2, arg3); + } + + public PowerException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public PowerException(String arg0) { + super(arg0); + } + + public PowerException(Throwable arg0) { + super(arg0); + } + +} diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPower.java b/edge/src/io/openems/core/utilities/power/SymmetricPower.java new file mode 100644 index 00000000000..528a8748070 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/SymmetricPower.java @@ -0,0 +1,397 @@ +package io.openems.core.utilities.power; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import com.vividsolutions.jts.awt.ShapeWriter; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.operation.distance.DistanceOp; +import com.vividsolutions.jts.operation.distance.GeometryLocation; +import com.vividsolutions.jts.util.GeometricShapeFactory; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.core.utilities.ControllerUtils; + +public class SymmetricPower { + + private final long maxApparentPower; + private final GeometryFactory factory; + private final Point zero; + private Geometry geometry; + private final ShapeWriter sw; + private final List geometries; + private final SVGWriter writer; + private static final Color[] colors = new Color[] { Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW, + Color.ORANGE, Color.RED }; + private ReadChannel currentActivePower; + private ReadChannel currentReactivePower; + private ReadChannel allowedApparent; + private ReadChannel allowedCharge; + private ReadChannel allowedDischarge; + private WriteChannel setActivePower; + private WriteChannel setReactivePower; + + public SymmetricPower(long maxApparentPower, ReadChannel currentActivePower, + ReadChannel currentReactivePower, ReadChannel allowedApparent, ReadChannel allowedCharge, + ReadChannel allowedDischarge, WriteChannel setActivePower, + WriteChannel setReactivePower) throws PowerException { + this.maxApparentPower = Math.abs(maxApparentPower); + this.currentActivePower = currentActivePower; + this.currentReactivePower = currentReactivePower; + this.allowedApparent = allowedApparent; + this.allowedCharge = allowedCharge; + this.allowedDischarge = allowedDischarge; + this.setActivePower = setActivePower; + this.setReactivePower = setReactivePower; + this.factory = new GeometryFactory(); + this.zero = factory.createPoint(new Coordinate(0, 0)); + this.sw = new ShapeWriter(); + this.writer = new SVGWriter(); + this.geometries = new ArrayList<>(); + reset(); + } + + public Optional getCurrentP() { + return this.currentActivePower.valueOptional(); + } + + public Optional getCurrentQ() { + return this.currentReactivePower.valueOptional(); + } + + public Optional getCurrentS() { + if (getCurrentP().isPresent() && getCurrentQ().isPresent()) { + return Optional.of(ControllerUtils.calculateApparentPower(getCurrentP().get(), getCurrentQ().get())); + } else { + return Optional.empty(); + } + } + + private void setGeometry(Geometry g) { + this.geometry = g; + this.geometries.add(g); + } + + public void reset() throws PowerException { + this.geometries.clear(); + GeometricShapeFactory shape = new GeometricShapeFactory(factory); + shape.setCentre(new Coordinate(0, 0)); + shape.setSize(this.maxApparentPower * 2); + shape.setNumPoints(32); + setGeometry(shape.createCircle()); + setSMax(this.allowedApparent.valueOptional().orElse(0L)); + setPGreaterOrEqual(this.allowedCharge.valueOptional().orElse(0L)); + setPSmallerOrEqual(this.allowedDischarge.valueOptional().orElse(0L)); + } + + public void setSMax(long smax) throws PowerException { + GeometricShapeFactory shape = new GeometricShapeFactory(factory); + shape.setCentre(new Coordinate(0, 0)); + shape.setSize(this.maxApparentPower); + shape.setNumPoints(32); + Geometry newGeometry = geometry.intersection(shape.createCircle()); + if (newGeometry.isEmpty()) { + // TODO throw exception + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + public void setPGreaterOrEqual(long p) throws PowerException { + Geometry newGeometry = intersectRect(this.geometry, p - 0.1, maxApparentPower, maxApparentPower * -1, + maxApparentPower); + if (newGeometry.isEmpty()) { + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + public void setPSmallerOrEqual(long p) throws PowerException { + Geometry newGeometry = intersectRect(this.geometry, maxApparentPower * -1, p + 0.1, maxApparentPower * -1, + maxApparentPower); + if (newGeometry.isEmpty()) { + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + public void setQGreaterOrEqual(long q) throws PowerException { + Geometry newGeometry = intersectRect(this.geometry, maxApparentPower * -1, maxApparentPower, q - 0.1, + maxApparentPower); + if (newGeometry.isEmpty()) { + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + public void setQSmallerOrEqual(long q) throws PowerException { + Geometry newGeometry = intersectRect(this.geometry, maxApparentPower * -1, maxApparentPower, + maxApparentPower * -1, q + 0.1); + if (newGeometry.isEmpty()) { + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + public void setNoQBetween(long qmin, long qmax) throws PowerException { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, qmin), + new Coordinate(maxApparentPower * -1, qmin), new Coordinate(maxApparentPower * -1, qmax), + new Coordinate(maxApparentPower, qmax), new Coordinate(maxApparentPower, qmin) }; + Geometry rect = this.factory.createPolygon(coordinates); + Geometry newGeometry = this.geometry.difference(rect); + if (newGeometry.isEmpty()) { + // TODO throw exception + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + /** + * + * @param pmin + * is exclusive + * @param pmax + * is exclusive + * @throws PowerException + */ + public void setNoPBetween(long pmin, long pmax) throws PowerException { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pmin, maxApparentPower), + new Coordinate(pmin, maxApparentPower * -1), new Coordinate(pmax, maxApparentPower * -1), + new Coordinate(pmax, maxApparentPower), new Coordinate(pmin, maxApparentPower) }; + Geometry rect = this.factory.createPolygon(coordinates); + Geometry newGeometry = this.geometry.difference(rect); + if (newGeometry.isEmpty()) { + // TODO throw exception + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + public void setMaxCosPhi(double cosPhi) throws PowerException { + double m = Math.tan(Math.acos(cosPhi)); + double y = m * maxApparentPower; + Coordinate[] coordinates = new Coordinate[] { new Coordinate(0, 0), new Coordinate(maxApparentPower, y), + new Coordinate(maxApparentPower, y * -1), new Coordinate(0, 0), + new Coordinate(maxApparentPower * -1, y * -1), new Coordinate(maxApparentPower * -1, y), + new Coordinate(0, 0) }; + Geometry polygon = this.factory.createPolygon(coordinates); + Geometry newGeometry = this.geometry.intersection(polygon); + if (newGeometry.isEmpty()) { + // TODO throw exception + throw new PowerException(""); + } + setGeometry(newGeometry); + } + + private Geometry intersectRect(Geometry base, double pMin, double pMax, double qMin, double qMax) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), + new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; + Geometry rect = this.factory.createPolygon(coordinates); + return this.geometry.intersection(rect); + } + + public void setP(long p) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(p, maxApparentPower), + new Coordinate(p, maxApparentPower * -1) }; + LineString line = this.factory.createLineString(coordinates); + Geometry newGeometry = this.geometry.intersection(line); + if (newGeometry.isEmpty()) { + Geometry smallerP = intersectRect(this.geometry, 0, p, maxApparentPower * -1, maxApparentPower); + if (!smallerP.isEmpty()) { + DistanceOp distance = new DistanceOp(smallerP, line); + GeometryLocation[] locations = distance.nearestLocations(); + long maxP = 0; + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + maxP = (long) location.getCoordinate().x; + break; + } + } + coordinates = new Coordinate[] { new Coordinate(maxP, maxApparentPower), + new Coordinate(maxP, maxApparentPower * -1) }; + line = this.factory.createLineString(coordinates); + setGeometry(this.geometry.intersection(line)); + } else { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + coordinates = new Coordinate[] { new Coordinate(location.getCoordinate().x, maxApparentPower), + new Coordinate(location.getCoordinate().x, maxApparentPower * -1) }; + line = this.factory.createLineString(coordinates); + setGeometry(this.geometry.intersection(line)); + break; + } + } + } + } else { + setGeometry(newGeometry); + } + } + + public void setQ(long q) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, q), + new Coordinate(maxApparentPower * -1, q) }; + LineString line = this.factory.createLineString(coordinates); + Geometry newGeometry = this.geometry.intersection(line); + if (newGeometry.isEmpty()) { + Geometry smallerQ = intersectRect(this.geometry, maxApparentPower * -1, maxApparentPower, 0, q); + if (!smallerQ.isEmpty()) { + DistanceOp distance = new DistanceOp(smallerQ, line); + GeometryLocation[] locations = distance.nearestLocations(); + long maxQ = 0; + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + maxQ = (long) location.getCoordinate().y; + break; + } + } + coordinates = new Coordinate[] { new Coordinate(maxApparentPower, maxQ), + new Coordinate(maxApparentPower * -1, maxQ) }; + line = this.factory.createLineString(coordinates); + setGeometry(this.geometry.intersection(line)); + } else { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + coordinates = new Coordinate[] { new Coordinate(maxApparentPower, location.getCoordinate().y), + new Coordinate(maxApparentPower * -1, location.getCoordinate().y) }; + line = this.factory.createLineString(coordinates); + setGeometry(this.geometry.intersection(line)); + break; + } + } + } + } else { + setGeometry(newGeometry); + } + } + + public void setPQ(double m, long t) { + double y1 = m * (maxApparentPower * -1) + t; + double y2 = m * maxApparentPower + t; + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower * -1, y1), + new Coordinate(maxApparentPower, y2) }; + LineString line = this.factory.createLineString(coordinates); + Geometry newGeometry = this.geometry.intersection(line); + if (newGeometry.isEmpty()) { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + setGeometry(this.factory.createPoint(location.getCoordinate())); + break; + } + } + } else { + setGeometry(newGeometry); + } + } + + public void printPower() { + JFrame frame = new JFrame("asdf"); + frame.setVisible(true); + frame.setSize(600, 400); + JPanel panel = new JPanel() { + @Override + protected void paintComponent(java.awt.Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.translate((int) maxApparentPower, (int) maxApparentPower); + int i = 0; + for (Geometry geo : geometries) { + g2.draw(sw.toShape(geo)); + i++; + i %= colors.length; + g2.setColor(colors[i]); + } + g2.translate((int) maxApparentPower * -1, (int) maxApparentPower * -1); + }; + }; + panel.setLayout(null); + frame.add(panel); + frame.validate(); + frame.repaint(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + public long getP() { + return (long) this.reduceToZero().getCoordinate().y; + } + + public long getQ() { + return (long) this.reduceToZero().getCoordinate().y; + } + + private Point reduceToZero() { + if (this.geometry instanceof Point) { + return (Point) this.geometry; + } + DistanceOp distance = new DistanceOp(geometry, this.zero); + GeometryLocation[] locations = distance.nearestLocations(); + Point result = this.zero; + for (GeometryLocation location : locations) { + Geometry g = location.getGeometryComponent(); + if (!g.equals(this.zero) && g instanceof Point) { + result = (Point) location.getGeometryComponent(); + break; + } + } + setGeometry(result); + return result; + } + + public String getAsSVG() { + StringBuffer text = new StringBuffer(); + + String viewBox = this.maxApparentPower * -1 + " " + this.maxApparentPower * -1 + " " + this.maxApparentPower * 2 + + " " + this.maxApparentPower * 2; + + text.append("\n"); + text.append( + "\n"); + text.append("\n"); + // String name = testable.getName() == null ? "" : testable.getName(); + // String description = testable.getDescription() == null ? "" : testable.getDescription(); + // text.append(" \"" + name + "\",\n"); + // text.append(" " + description + "\n"); + int i = 0; + for (Geometry geo : geometries) { + String a = writeGeometryStyled(geo, "#" + Integer.toHexString(colors[i].getRGB()).substring(2)); + text.append(a + "\n"); + text.append("\n"); + i++; + i %= colors.length; + } + text.append("\n"); + return text.toString(); + } + + private String writeGeometryStyled(Geometry g, String fillClr) { + String s = "\n"; + s += write(g); + s += ""; + return s; + } + + private String write(Geometry geometry) { + if (geometry == null) { + return ""; + } + return writer.write(geometry); + } + +} From f372c5d3af80c09d5a1a0aca54cc1bd4bd675323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 16 Jan 2018 15:18:06 +0100 Subject: [PATCH 007/156] fix cosPhi calculations --- .../core/utilities/ControllerUtils.java | 372 +++++++++--------- .../balancing/BalancingController.java | 5 +- .../BalancingBandgapController.java | 244 ++++++------ .../BalancingCosPhiController.java | 154 ++++---- .../symmetric/balancingcosphi/Ess.java | 108 ++--- .../symmetric/cosphi/CosPhiController.java | 150 +++---- .../CosPhiCharacteristicController.java | 198 +++++----- .../impl/device/spanner/BHKWMeter.java | 6 +- 8 files changed, 638 insertions(+), 599 deletions(-) diff --git a/edge/src/io/openems/core/utilities/ControllerUtils.java b/edge/src/io/openems/core/utilities/ControllerUtils.java index d8628fb7dbc..0b29cb8f8b7 100644 --- a/edge/src/io/openems/core/utilities/ControllerUtils.java +++ b/edge/src/io/openems/core/utilities/ControllerUtils.java @@ -1,183 +1,189 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.core.utilities; - -import java.util.Collections; -import java.util.List; - -import io.openems.api.channel.WriteChannel; - -public class ControllerUtils { - - public static double calculateCosPhi(long activePower, long reactivePower) { - if (reactivePower == 0) { - return 1; - } - return Math.cos(Math.atan((double) activePower / (double) reactivePower)); - } - - public static long calculateReactivePower(long activePower, double cosPhi) { - return (long) (Math.tan(Math.acos(cosPhi)) * activePower); - } - - public static long calculateReactivePower(long activePower, long apparentPower) { - return (long) Math.sqrt(Math.pow(apparentPower, 2) - Math.pow(activePower, 2)); - } - - public static long calculateActivePower(long reactivePower, long apparentPower) { - return (long) Math.sqrt(Math.pow(apparentPower, 2) - Math.pow(reactivePower, 2)); - } - - public static long calculateApparentPower(long activePower, long reactivePower) { - return (long) Math.sqrt(Math.pow(activePower, 2) + Math.pow(reactivePower, 2)); - } - - public static long calculateActivePowerFromApparentPower(long apparentPower, double cosPhi) { - return (long) (apparentPower * cosPhi); - } - - public static long calculateActivePowerFromReactivePower(long reactivePower, double cosPhi) { - return (long) (reactivePower / Math.tan(Math.acos(cosPhi))); - } - - public static long calculateApparentPower(long activePower, double cosPhi) { - return (long) (activePower / cosPhi); - } - - public static long reduceActivePower(long activePower, long reactivePower, long maxChargeApparentPower, - long maxDischargeApparentPower) { - boolean activePowerPos = true; - - if (activePower < 0) { - activePowerPos = false; - } - - if (isCharge(activePower, reactivePower)) { - /* - * Charge - */ - if (calculateApparentPower(activePower, reactivePower) * -1 < maxChargeApparentPower) { - double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); - activePower = ControllerUtils.calculateActivePowerFromApparentPower(maxChargeApparentPower, cosPhi); - } - } else { - - /* - * Discharge - */ - if (ControllerUtils.calculateApparentPower(activePower, reactivePower) > maxDischargeApparentPower) { - double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); - activePower = ControllerUtils.calculateActivePowerFromApparentPower(maxDischargeApparentPower, cosPhi); - } - } - if (!activePowerPos && activePower >= 0) { - activePower *= -1; - } - - return activePower; - } - - public static long reduceReactivePower(long activePower, long reactivePower, long maxChargeApparentPower, - long maxDischargeApparentPower, long maxApparent, WriteChannel setReactivePower) { - boolean reactivePowerPos = true; - if (reactivePower < 0) { - reactivePowerPos = false; - } - if (isCharge(activePower, reactivePower)) { - /* - * Charge - */ - if (calculateApparentPower(activePower, reactivePower) * -1 < maxChargeApparentPower) { - double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); - reactivePower = ControllerUtils.calculateReactivePower(reduceActivePower(activePower, reactivePower, - maxChargeApparentPower, maxDischargeApparentPower), cosPhi); - } - } else { - - /* - * Discharge - */ - if (ControllerUtils.calculateApparentPower(activePower, reactivePower) > maxDischargeApparentPower) { - double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); - reactivePower = ControllerUtils.calculateReactivePower(reduceActivePower(activePower, reactivePower, - maxChargeApparentPower, maxDischargeApparentPower), cosPhi); - } - } - if (!reactivePowerPos && reactivePower >= 0) { - reactivePower *= -1; - } - return reactivePower; - } - - public static boolean isCharge(long activePower, long reactivePower) { - if (activePower >= 0 && reactivePower >= 0) { - return false; - } else if (activePower < 0 && reactivePower >= 0) { - return true; - } else if (activePower < 0 && reactivePower < 0) { - return true; - } else { - return false; - } - } - - public static double getValueOfLine(List points, double x) { - Point smaller = getSmallerPoint(points, x); - Point greater = getGreaterPoint(points, x); - if (smaller != null && greater == null) { - return smaller.y; - } else if (smaller == null && greater != null) { - return greater.y; - } else if (smaller != null && greater != null) { - if (smaller.equals(greater)) { - return smaller.y; - } else { - double m = (greater.y - smaller.y) / (greater.x - smaller.x); - double t = smaller.y - m * smaller.x; - return m * x + t; - } - } else { - throw new ArithmeticException("x[" + x + "] is out of Range of the points"); - } - } - - private static Point getGreaterPoint(List points, double x) { - Collections.sort(points, (p1, p2) -> (int) (p1.x - p2.x)); - for (int i = 0; i < points.size(); i++) { - Point p = points.get(i); - if (p.x >= x) { - return p; - } - } - return null; - } - - private static Point getSmallerPoint(List points, double x) { - Collections.sort(points, (p1, p2) -> (int) (p2.x - p1.x)); - for (int i = 0; i < points.size(); i++) { - Point p = points.get(i); - if (p.x <= x) { - return p; - } - } - return null; - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.core.utilities; + +import java.util.Collections; +import java.util.List; + +public class ControllerUtils { + + public static double calculateCosPhi(long activePower, long reactivePower) { + return activePower / Math.sqrt(Math.pow(activePower, 2) + Math.pow(reactivePower, 2)); + } + + public static long calculateReactivePower(long activePower, long apparentPower) { + return (long) Math.sqrt(Math.pow(apparentPower, 2) - Math.pow(activePower, 2)); + } + + public static long calculateReactivePower(long activePower, double cosPhi, boolean capacitive) { + long apparentPower = calculateApparentPower(activePower, cosPhi); + long reactivePower = calculateReactivePower(activePower, apparentPower); + // differe between capacitive and inductive CosPhi + if (capacitive) { + reactivePower *= -1; + } + return reactivePower; + } + + public static long calculateActivePower(long reactivePower, long apparentPower) { + return (long) Math.sqrt(Math.pow(apparentPower, 2) - Math.pow(reactivePower, 2)); + } + + public static long calculateApparentPower(long activePower, long reactivePower) { + return (long) Math.sqrt(Math.pow(activePower, 2) + Math.pow(reactivePower, 2)); + } + + /** + * Calculates the absolute activePower according to the cosPhi + * + * @param apparentPower + * @param cosPhi + * @return returns the absolute activePower + */ + public static long calculateActivePowerFromApparentPower(long apparentPower, double cosPhi) { + return (long) (apparentPower * cosPhi); + } + + // + // public static long calculateActivePowerFromReactivePower(long reactivePower, double cosPhi) { + // return (long) (reactivePower / Math.tan(Math.acos(cosPhi))); + // } + // + public static long calculateApparentPower(long activePower, double cosPhi) { + return (long) (activePower / cosPhi); + } + + // + // public static long reduceActivePower(long activePower, long reactivePower, long maxChargeApparentPower, + // long maxDischargeApparentPower) { + // boolean activePowerPos = true; + // + // if (activePower < 0) { + // activePowerPos = false; + // } + // + // if (isCharge(activePower, reactivePower)) { + // /* + // * Charge + // */ + // if (calculateApparentPower(activePower, reactivePower) * -1 < maxChargeApparentPower) { + // double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); + // activePower = ControllerUtils.calculateActivePowerFromApparentPower(maxChargeApparentPower, cosPhi); + // } + // } else { + // + // /* + // * Discharge + // */ + // if (ControllerUtils.calculateApparentPower(activePower, reactivePower) > maxDischargeApparentPower) { + // double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); + // activePower = ControllerUtils.calculateActivePowerFromApparentPower(maxDischargeApparentPower, cosPhi); + // } + // } + // if (!activePowerPos && activePower >= 0) { + // activePower *= -1; + // } + // + // return activePower; + // } + // + // public static long reduceReactivePower(long activePower, long reactivePower, long maxChargeApparentPower, + // long maxDischargeApparentPower, long maxApparent, WriteChannel setReactivePower) { + // boolean reactivePowerPos = true; + // if (reactivePower < 0) { + // reactivePowerPos = false; + // } + // if (isCharge(activePower, reactivePower)) { + // /* + // * Charge + // */ + // if (calculateApparentPower(activePower, reactivePower) * -1 < maxChargeApparentPower) { + // double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); + // reactivePower = ControllerUtils.calculateReactivePower(reduceActivePower(activePower, reactivePower, + // maxChargeApparentPower, maxDischargeApparentPower), cosPhi); + // } + // } else { + // + // /* + // * Discharge + // */ + // if (ControllerUtils.calculateApparentPower(activePower, reactivePower) > maxDischargeApparentPower) { + // double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); + // reactivePower = ControllerUtils.calculateReactivePower(reduceActivePower(activePower, reactivePower, + // maxChargeApparentPower, maxDischargeApparentPower), cosPhi); + // } + // } + // if (!reactivePowerPos && reactivePower >= 0) { + // reactivePower *= -1; + // } + // return reactivePower; + // } + // + public static boolean isCharge(long activePower, long reactivePower) { + if (activePower >= 0) { + return false; + } else { + return true; + } + } + + public static double getValueOfLine(List points, double x) { + Point smaller = getSmallerPoint(points, x); + Point greater = getGreaterPoint(points, x); + if (smaller != null && greater == null) { + return smaller.y; + } else if (smaller == null && greater != null) { + return greater.y; + } else if (smaller != null && greater != null) { + if (smaller.equals(greater)) { + return smaller.y; + } else { + double m = (greater.y - smaller.y) / (greater.x - smaller.x); + double t = smaller.y - m * smaller.x; + return m * x + t; + } + } else { + throw new ArithmeticException("x[" + x + "] is out of Range of the points"); + } + } + + private static Point getGreaterPoint(List points, double x) { + Collections.sort(points, (p1, p2) -> (int) (p1.x - p2.x)); + for (int i = 0; i < points.size(); i++) { + Point p = points.get(i); + if (p.x >= x) { + return p; + } + } + return null; + } + + private static Point getSmallerPoint(List points, double x) { + Collections.sort(points, (p1, p2) -> (int) (p2.x - p1.x)); + for (int i = 0; i < points.size(); i++) { + Point p = points.get(i); + if (p.x <= x) { + return p; + } + } + return null; + } +} diff --git a/edge/src/io/openems/impl/controller/asymmetric/balancing/BalancingController.java b/edge/src/io/openems/impl/controller/asymmetric/balancing/BalancingController.java index 871aa531c83..88811efe0d8 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/balancing/BalancingController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/balancing/BalancingController.java @@ -58,6 +58,9 @@ public BalancingController(String thingId) { @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) public ConfigChannel meter = new ConfigChannel("meter", this); + @ChannelInfo(title = "Capacitive CosPhi", description="if this value is true the cosPhi is capacitive otherwise inductive.",type=Boolean.class, defaultValue="false") + public ConfigChannel capacitive = new ConfigChannel("capacitive",this); + /* * Fields */ @@ -260,7 +263,7 @@ private void calculatePower(long calculatedPower, long maxDischargePower, long m .ceil(minPower + diff / (esss.value().size() * 100 - useableSoc) * (100 - ess.useableSoc()))); } - long reactivePower = ControllerUtils.calculateReactivePower(power, cosPhi.value()); + long reactivePower = ControllerUtils.calculateReactivePower(power, cosPhi.value(),capacitive.value()); calculatedPower -= power; diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java index 475b9d5f4de..9db242c4e3f 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java @@ -1,117 +1,127 @@ -/******************************************************************************* - * 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.symmetric.balancingbandgap; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; - -@ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") -public class BalancingBandgapController extends Controller { - - /* - * Constructors - */ - public BalancingBandgapController() { - super(); - } - - public BalancingBandgapController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) - public final ConfigChannel meter = new ConfigChannel("meter", this); - - @ChannelInfo(title = "Min-ActivePower", description = "Low boundary of active power bandgap.", type = Integer.class) - public final ConfigChannel minActivePower = new ConfigChannel<>("minActivePower", this); - - @ChannelInfo(title = "Max-ActivePower", description = "High boundary of active power bandgap.", type = Integer.class) - public final ConfigChannel maxActivePower = new ConfigChannel<>("maxActivePower", this); - - @ChannelInfo(title = "Min-ReactivePower", description = "Low boundary of reactive power bandgap.", type = Integer.class) - public final ConfigChannel minReactivePower = new ConfigChannel<>("minReactivePower", this); - - @ChannelInfo(title = "Max-ReactivePower", description = "High boundary of reactive power bandgap.", type = Integer.class) - public final ConfigChannel maxReactivePower = new ConfigChannel<>("maxReactivePower", this); - - @ChannelInfo(title = "Enable ActivePower", description = "Indicates if active power bandgap is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel activePowerActivated = new ConfigChannel("activePowerActivated", this); - - @ChannelInfo(title = "Enable ReactivePower", description = "Indicates if reactive power bandgap is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel reactivePowerActivated = new ConfigChannel("reactivePowerActivated", - this); - - /* - * Methods - */ - @Override - public void run() { - try { - Ess ess = this.ess.value(); - Meter meter = this.meter.value(); - // Calculate required sum values - long calculatedPower = meter.activePower.value() + ess.activePower.value(); - long calculatedReactivePower = meter.reactivePower.value() + ess.reactivePower.value(); - if (calculatedPower >= maxActivePower.value()) { - calculatedPower -= maxActivePower.value(); - } else if (calculatedPower <= minActivePower.value()) { - calculatedPower -= minActivePower.value(); - } else { - calculatedPower = 0; - } - if (calculatedReactivePower >= maxReactivePower.value()) { - calculatedReactivePower -= maxReactivePower.value(); - } else if (calculatedReactivePower <= minReactivePower.value()) { - calculatedReactivePower -= minReactivePower.value(); - } else { - calculatedReactivePower = 0; - } - if (reactivePowerActivated.value()) { - ess.power.setReactivePower(calculatedReactivePower); - } - if (activePowerActivated.value()) { - ess.power.setActivePower(calculatedPower); - } - ess.power.writePower(); - // write info message to log - String message = ess.id(); - if (activePowerActivated.value()) { - message = message + " Set ActivePower [" + ess.power.getActivePower() + "]"; - } - if (reactivePowerActivated.value()) { - message = message + " Set ReactivePower [" + ess.power.getReactivePower() + "]"; - } - log.info(message); - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - -} +/******************************************************************************* + * 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.symmetric.balancingbandgap; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.AvgFiFoQueue; + +@ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") +public class BalancingBandgapController extends Controller { + + /* + * Constructors + */ + public BalancingBandgapController() { + super(); + } + + public BalancingBandgapController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Min-ActivePower", description = "Low boundary of active power bandgap.", type = Integer.class) + public final ConfigChannel minActivePower = new ConfigChannel<>("minActivePower", this); + + @ChannelInfo(title = "Max-ActivePower", description = "High boundary of active power bandgap.", type = Integer.class) + public final ConfigChannel maxActivePower = new ConfigChannel<>("maxActivePower", this); + + @ChannelInfo(title = "Min-ReactivePower", description = "Low boundary of reactive power bandgap.", type = Integer.class) + public final ConfigChannel minReactivePower = new ConfigChannel<>("minReactivePower", this); + + @ChannelInfo(title = "Max-ReactivePower", description = "High boundary of reactive power bandgap.", type = Integer.class) + public final ConfigChannel maxReactivePower = new ConfigChannel<>("maxReactivePower", this); + + @ChannelInfo(title = "Enable ActivePower", description = "Indicates if active power bandgap is enabled.", type = Boolean.class, defaultValue = "true") + public final ConfigChannel activePowerActivated = new ConfigChannel("activePowerActivated", this); + + @ChannelInfo(title = "Enable ReactivePower", description = "Indicates if reactive power bandgap is enabled.", type = Boolean.class, defaultValue = "true") + public final ConfigChannel reactivePowerActivated = new ConfigChannel("reactivePowerActivated", + this); + + private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(2, 1.5); + private AvgFiFoQueue meterReactivePower = new AvgFiFoQueue(2, 1.5); + private AvgFiFoQueue essActivePower = new AvgFiFoQueue(2, 1.5); + private AvgFiFoQueue essReactivePower = new AvgFiFoQueue(2, 1.5); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + Meter meter = this.meter.value(); + meterActivePower.add(meter.activePower.value()); + meterReactivePower.add(meter.reactivePower.value()); + essActivePower.add(ess.activePower.value()); + essReactivePower.add(ess.reactivePower.value()); + // Calculate required sum values + long calculatedPower = meterActivePower.avg() + essActivePower.avg(); + long calculatedReactivePower = meterReactivePower.avg() + essReactivePower.avg(); + if (calculatedPower >= maxActivePower.value()) { + calculatedPower -= maxActivePower.value(); + } else if (calculatedPower <= minActivePower.value()) { + calculatedPower -= minActivePower.value(); + } else { + calculatedPower = 0; + } + if (calculatedReactivePower >= maxReactivePower.value()) { + calculatedReactivePower -= maxReactivePower.value(); + } else if (calculatedReactivePower <= minReactivePower.value()) { + calculatedReactivePower -= minReactivePower.value(); + } else { + calculatedReactivePower = 0; + } + if (reactivePowerActivated.value()) { + ess.power.setReactivePower(calculatedReactivePower); + } + if (activePowerActivated.value()) { + ess.power.setActivePower(calculatedPower); + } + ess.power.writePower(); + // write info message to log + String message = ess.id(); + if (activePowerActivated.value()) { + message = message + " Set ActivePower [" + ess.power.getActivePower() + "]"; + } + if (reactivePowerActivated.value()) { + message = message + " Set ReactivePower [" + ess.power.getReactivePower() + "]"; + } + log.info(message); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java index 27967cd51d4..81b225b0aaa 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java @@ -1,73 +1,81 @@ -/******************************************************************************* - * 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.symmetric.balancingcosphi; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; - -@ThingInfo(title = "Balancing Cos-Phi (Symmetric)", description = "Tries to keep the grid meter at a given cos-phi. For symmetric Ess.") -public class BalancingCosPhiController extends Controller { - - /* - * Constructors - */ - public BalancingCosPhiController() { - super(); - } - - public BalancingCosPhiController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) - public ConfigChannel meter = new ConfigChannel("meter", this); - - @ChannelInfo(title = "Cos-Phi", description = "Cos-phi which the grid-meter is trying to hold.", type = Double.class) - public ConfigChannel cosPhi = new ConfigChannel("cosPhi", this); - - /* - * Methods - */ - @Override - public void run() { - try { - double cosPhi = this.cosPhi.value(); - double phi = Math.acos(cosPhi); - long q = (long) ((meter.value().activePower.value() * Math.tan(phi)) - meter.value().reactivePower.value()) - * -1; - q += ess.value().reactivePower.value(); - ess.value().power.setReactivePower(q); - ess.value().power.writePower(); - log.info(ess.id() + " Set ReactivePower [" + ess.value().power.getReactivePower() + "]"); - } catch (InvalidValueException e) { - log.error("Failed to read value.", e); - } - } -} +/******************************************************************************* + * 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.symmetric.balancingcosphi; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.ControllerUtils; + +@ThingInfo(title = "Balancing Cos-Phi (Symmetric)", description = "Tries to keep the grid meter at a given cos-phi. For symmetric Ess.") +public class BalancingCosPhiController extends Controller { + + /* + * Constructors + */ + public BalancingCosPhiController() { + super(); + } + + public BalancingCosPhiController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Cos-Phi", description = "Cos-phi which the grid-meter is trying to hold.", type = Double.class) + public ConfigChannel cosPhi = new ConfigChannel("cosPhi", this); + + @ChannelInfo(title = "Capacitive CosPhi", description="if this value is true the cosPhi is capacitive otherwise inductive.",type=Boolean.class) + public ConfigChannel capacitive = new ConfigChannel("capacitive",this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + Meter meter = this.meter.value(); + long currentActivePowerEss = ess.activePower.value();//50 + long currentReactivePowerEss = ess.activePower.value();//10 + long currentActivePowerGrid = meter.activePower.value();//-10 + long currentReactivePowerGrid = meter.reactivePower.value();//5 + long expectedActivePowerGrid = currentActivePowerGrid-(ess.setActivePower.getWriteValue().orElse(currentActivePowerEss)-currentActivePowerEss);//-10-(-2-50)=-62 + long expectedReactivePowerGrid = ControllerUtils.calculateReactivePower(expectedActivePowerGrid, cosPhi.value(),capacitive.value());//30,027 + long q = currentReactivePowerEss - (expectedReactivePowerGrid - currentReactivePowerGrid);//10-(30,027-5)=-15,02 + ess.power.setReactivePower(q); + ess.power.writePower(); + log.info(ess.id() + " Set ReactivePower [" + ess.power.getReactivePower() + "]"); + } catch (InvalidValueException e) { + log.error("Failed to read value.", e); + } + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java index 7035711a488..c50dd585926 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java @@ -1,53 +1,55 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.balancingcosphi; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel reactivePower; - public final String id; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower(); - setReactivePower = ess.setReactivePower(); - id = ess.id(); - allowedCharge = ess.allowedCharge(); - allowedDischarge = ess.allowedDischarge(); - reactivePower = ess.reactivePower(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.balancingcosphi; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.core.utilities.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final WriteChannel setActivePower; + public final WriteChannel setReactivePower; + public final ReadChannel reactivePower; + public final ReadChannel activePower; + public final String id; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final SymmetricPower power; + + public Ess(SymmetricEssNature ess) { + super(ess); + setActivePower = ess.setActivePower(); + setReactivePower = ess.setReactivePower(); + id = ess.id(); + allowedCharge = ess.allowedCharge(); + allowedDischarge = ess.allowedDischarge(); + reactivePower = ess.reactivePower(); + activePower = ess.activePower(); + this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), + ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java index d5689ee6296..d85104d57eb 100644 --- a/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java +++ b/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java @@ -1,72 +1,78 @@ -/******************************************************************************* - * 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.symmetric.cosphi; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.ControllerUtils; - -@ThingInfo(title = "Ess Cos-Phi (Symmetric)", description = "Keeps the Ess at a given cos-phi. For symmetric Ess.") -public class CosPhiController extends Controller { - - /* - * Constructors - */ - public CosPhiController() { - super(); - } - - public CosPhiController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Cos-Phi", description = "The cos-phi to hold on the storage.", type = Double.class) - public ConfigChannel cosPhi = new ConfigChannel("cosPhi", this); - - /* - * Methods - */ - @Override - public void run() { - try { - if (ess.value().setActivePower.peekWrite().isPresent()) { - ess.value().power.setReactivePower(ControllerUtils - .calculateReactivePower(ess.value().setActivePower.peekWrite().get(), cosPhi.value())); - ess.value().power.writePower(); - log.info("Set ReactivePower [" + ess.value().power.getReactivePower() + "]"); - } else { - log.error(ess.id() + " no ActivePower is Set."); - } - } catch (InvalidValueException e) { - log.error("No ess found.", e); - } - } - -} +/******************************************************************************* + * 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.symmetric.cosphi; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.ControllerUtils; +import io.openems.impl.controller.symmetric.balancingcosphi.Ess; + +@ThingInfo(title = "Ess Cos-Phi (Symmetric)", description = "Keeps the Ess at a given cos-phi. For symmetric Ess.") +public class CosPhiController extends Controller { + + /* + * Constructors + */ + public CosPhiController() { + super(); + } + + public CosPhiController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Cos-Phi", description = "The cos-phi to hold on the storage.", type = Double.class) + public ConfigChannel cosPhi = new ConfigChannel("cosPhi", this); + + @ChannelInfo(title = "Capacitive CosPhi", description="if this value is true the cosPhi is capacitive otherwise inductive.",type=Boolean.class) + public ConfigChannel capacitive = new ConfigChannel("capacitive",this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + if (ess.setActivePower.peekWrite().isPresent()) { + long expectedActivePower = ess.setActivePower.peekWrite().get();//40 + long q = ControllerUtils.calculateReactivePower(expectedActivePower, cosPhi.value(), capacitive.value()); + ess.power.setReactivePower(q); + ess.power.writePower(); + log.info("Set ReactivePower [" + ess.power.getReactivePower() + "]"); + } else { + log.error(ess.id() + " no ActivePower is Set."); + } + } catch (InvalidValueException e) { + log.error("No ess found.", e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java index da7d3f8fae9..f89b6ab2fc2 100644 --- a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java +++ b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java @@ -1,97 +1,101 @@ -/******************************************************************************* - * 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.symmetric.cosphicharacteristic; - -import java.util.ArrayList; -import java.util.List; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.ControllerUtils; -import io.openems.core.utilities.Point; - -@ThingInfo(title = "Cos-Phi Characteristics (Symmetric)") -public class CosPhiCharacteristicController extends Controller { - - /* - * Constructors - */ - public CosPhiCharacteristicController() { - super(); - } - - public CosPhiCharacteristicController(String id) { - super(id); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel<>("ess", this); - - @ChannelInfo(title = "Cos-Phi characteristic", description = "The points of the characteristic (x = PowerRatio, y = cosPhi).", type = Long[].class, isArray = true) - public ConfigChannel> cosPhiPoints = new ConfigChannel>("cosPhiPoints", this) - .addChangeListener((channel, newValue, oldValue) -> { - List points = new ArrayList<>(); - if (newValue.isPresent()) { - @SuppressWarnings("unchecked") List cosPhiPoints = (List) newValue.get(); - for (Long[] arr : cosPhiPoints) { - points.add(new Point(arr[0], arr[1])); - } - } else { - log.error("found no cosPhiPoints!"); - } - cosPhiCharacteristic = points; - }); - - /* - * Fields - */ - public List cosPhiCharacteristic; - - /* - * Methods - */ - @Override - public void run() { - try { - if (ess.value().setActivePower.peekWrite().isPresent()) { - double pRatio = (double) ess.value().setActivePower.peekWrite().get() - / (double) ess.value().nominalPower.value() * 100; - double cosPhi = ControllerUtils.getValueOfLine(cosPhiCharacteristic, pRatio) / 100; - ess.value().power.setReactivePower( - ControllerUtils.calculateReactivePower(ess.value().setActivePower.peekWrite().get(), cosPhi)); - ess.value().power.writePower(); - log.info("Set reactive power [{}] to get cosPhi [{}]", - new Object[] { ess.value().power.getReactivePower(), cosPhi }); - } else { - log.error(ess.id() + " no ActivePower is Set."); - } - } catch (InvalidValueException e) { - log.error("No ess found.", e); - } - } - -} +/******************************************************************************* + * 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.symmetric.cosphicharacteristic; + +import java.util.ArrayList; +import java.util.List; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.Point; + +@ThingInfo(title = "Cos-Phi Characteristics (Symmetric)") +public class CosPhiCharacteristicController extends Controller { + + /* + * Constructors + */ + public CosPhiCharacteristicController() { + super(); + } + + public CosPhiCharacteristicController(String id) { + super(id); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) + public ConfigChannel ess = new ConfigChannel<>("ess", this); + + @ChannelInfo(title = "Cos-Phi characteristic", description = "The points of the characteristic (x = signed activePower, y = cosPhi IEEE Power Factor Sign Convention ).", type = Long[].class, isArray = true) + public ConfigChannel> cosPhiPoints = new ConfigChannel>("cosPhiPoints", this) + .addChangeListener((channel, newValue, oldValue) -> { + List points = new ArrayList<>(); + if (newValue.isPresent()) { + @SuppressWarnings("unchecked") List cosPhiPoints = (List) newValue.get(); + for (Long[] arr : cosPhiPoints) { + points.add(new Point(arr[0], arr[1])); + } + } else { + log.error("found no cosPhiPoints!"); + } + cosPhiCharacteristic = points; + }); + + /* + * Fields + */ + public List cosPhiCharacteristic; + + /* + * Methods + */ + @Override + public void run() { + try { + if (ess.value().setActivePower.peekWrite().isPresent()) { + double pRatio = (double) ess.value().setActivePower.peekWrite().get() + / (double) ess.value().nominalPower.value() * 100; + double cosPhi = ControllerUtils.getValueOfLine(cosPhiCharacteristic, pRatio) / 100; + boolean capacitive = false; + if((pRatio<0 && cosPhi<0)||(pRatio>0 && cosPhi>0)) { + capacitive = true; + } + ess.value().power.setReactivePower( + ControllerUtils.calculateReactivePower(ess.value().setActivePower.peekWrite().get(), cosPhi,capacitive)); + ess.value().power.writePower(); + log.info("Set reactive power [{}] to get cosPhi [{}]", + new Object[] { ess.value().power.getReactivePower(), cosPhi }); + } else { + log.error(ess.id() + " no ActivePower is Set."); + } + } catch (InvalidValueException e) { + log.error("No ess found.", e); + } + } + +} diff --git a/edge/src/io/openems/impl/device/spanner/BHKWMeter.java b/edge/src/io/openems/impl/device/spanner/BHKWMeter.java index b262692ef76..634eb1b6571 100644 --- a/edge/src/io/openems/impl/device/spanner/BHKWMeter.java +++ b/edge/src/io/openems/impl/device/spanner/BHKWMeter.java @@ -214,7 +214,7 @@ public Long handle(@SuppressWarnings("unchecked") ReadChannel... channels) @Override public Long handle(@SuppressWarnings("unchecked") ReadChannel... channels) throws InvalidValueException { if(channels.length == 2 && channels[0].valueOptional().isPresent()&& channels[1].valueOptional().isPresent()) { - return ControllerUtils.calculateReactivePower(channels[0].valueOptional().get(), ((double)channels[1].valueOptional().get())/100.0); + return ControllerUtils.calculateReactivePower(channels[0].valueOptional().get(), ((double)channels[1].valueOptional().get())/100.0,false); } return null; } @@ -224,7 +224,7 @@ public Long handle(@SuppressWarnings("unchecked") ReadChannel... channels) @Override public Long handle(@SuppressWarnings("unchecked") ReadChannel... channels) throws InvalidValueException { if(channels.length == 2 && channels[0].valueOptional().isPresent()&& channels[1].valueOptional().isPresent()) { - return ControllerUtils.calculateReactivePower(channels[0].valueOptional().get(), ((double)channels[1].valueOptional().get())/100.0); + return ControllerUtils.calculateReactivePower(channels[0].valueOptional().get(), ((double)channels[1].valueOptional().get())/100.0,false); } return null; } @@ -234,7 +234,7 @@ public Long handle(@SuppressWarnings("unchecked") ReadChannel... channels) @Override public Long handle(@SuppressWarnings("unchecked") ReadChannel... channels) throws InvalidValueException { if(channels.length == 2 && channels[0].valueOptional().isPresent()&& channels[1].valueOptional().isPresent()) { - return ControllerUtils.calculateReactivePower(channels[0].valueOptional().get(), ((double)channels[1].valueOptional().get())/100.0); + return ControllerUtils.calculateReactivePower(channels[0].valueOptional().get(), ((double)channels[1].valueOptional().get())/100.0,false); } return null; } From 137fe9e1c04f41b4116049e6c2f797bf37912836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 16 Jan 2018 15:19:07 +0100 Subject: [PATCH 008/156] add ChannelThresholdScheduler template --- .../scheduler/ChannelThresholdScheduler.json | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 edge/template/scheduler/ChannelThresholdScheduler.json diff --git a/edge/template/scheduler/ChannelThresholdScheduler.json b/edge/template/scheduler/ChannelThresholdScheduler.json new file mode 100644 index 00000000000..138db7980b9 --- /dev/null +++ b/edge/template/scheduler/ChannelThresholdScheduler.json @@ -0,0 +1,58 @@ +"scheduler": { + "class": "io.openems.impl.scheduler.channelthreshold.ChannelThresholdScheduler", + "controllers": [ + { + "id": "rest", + "class": "io.openems.impl.controller.api.rest.RestApiController", + "priority": 150 + }, + { + "id": "websocketApi0", + "class": "io.openems.impl.controller.api.websocket.WebsocketApiController", + "priority": 100 + }, + { + "id": "charge", + "class": "io.openems.impl.controller.asymmetric.fixvalue.FixValueActivePowerController", + "esss": [ + "ess0" + ], + "priority": 150, + "activePowerL1": -2000, + "activePowerL2": -2000, + "activePowerL3": -2000 + }, + { + "id": "discharge", + "class": "io.openems.impl.controller.asymmetric.fixvalue.FixValueActivePowerController", + "esss": [ + "ess0" + ], + "priority": 150, + "activePowerL1": 2000, + "activePowerL2": 2000, + "activePowerL3": 2000 + } + ], + "always": [ + "websocketApi0", + "rest" + ], + "thresholds": [ + { + "threshold": 20, + "hysteresis": 60, + "controller": [ + "charge" + ] + }, + { + "threshold": 22, + "hysteresis": 60, + "controller": [ + "discharge" + ] + } + ], + "thresholdChannelAddress": "ess0/Soc" +} \ No newline at end of file From 69d5e973b6ab2d074cd23487ef44c9c63ab09022 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 25 Jan 2018 13:26:40 +0100 Subject: [PATCH 009/156] Add OSGi workspace + simple BackendApp --- .gitattributes | 75 ++++++++ .gitignore | 5 + .gradle-wrapper/gradle-wrapper.jar | Bin 0 -> 54708 bytes .gradle-wrapper/gradle-wrapper.properties | 5 + .travis.yml | 10 + build.gradle | 1 + cnf/.classpath | 6 + cnf/.gitignore | 4 + cnf/.project | 11 ++ cnf/build.bnd | 3 + cnf/central.xml | 19 ++ cnf/ext/enroute-distro.bnd | 49 +++++ cnf/ext/enroute.bnd | 43 +++++ cnf/release/index.xml | 3 + cnf/release/index.xml.sha | 1 + cnf/src/.gitignore | 0 gradle.properties | 1 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ io.openems.backend.application/.classpath | 8 + io.openems.backend.application/.gitignore | 3 + io.openems.backend.application/.project | 23 +++ .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 11 ++ io.openems.backend.application/bnd.bnd | 20 ++ .../io.openems.backend.application.bndrun | 20 ++ io.openems.backend.application/readme.md | 8 + .../backend/application/BackendApp.java | 20 ++ .../backend/application/ProviderImplTest.java | 26 +++ settings.gradle | 6 + 30 files changed, 643 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .gradle-wrapper/gradle-wrapper.jar create mode 100644 .gradle-wrapper/gradle-wrapper.properties create mode 100644 .travis.yml create mode 100644 build.gradle create mode 100644 cnf/.classpath create mode 100644 cnf/.gitignore create mode 100644 cnf/.project create mode 100644 cnf/build.bnd create mode 100644 cnf/central.xml create mode 100644 cnf/ext/enroute-distro.bnd create mode 100644 cnf/ext/enroute.bnd create mode 100644 cnf/release/index.xml create mode 100644 cnf/release/index.xml.sha create mode 100644 cnf/src/.gitignore create mode 100644 gradle.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 io.openems.backend.application/.classpath create mode 100644 io.openems.backend.application/.gitignore create mode 100644 io.openems.backend.application/.project create mode 100644 io.openems.backend.application/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.application/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.application/bnd.bnd create mode 100644 io.openems.backend.application/io.openems.backend.application.bndrun create mode 100644 io.openems.backend.application/readme.md create mode 100644 io.openems.backend.application/src/io/openems/backend/application/BackendApp.java create mode 100644 io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java create mode 100644 settings.gradle diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..b21186bf0bd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,75 @@ +# Text files with LF eol +*.auth crlf=input +*.awk crlf=input +*.bnd crlf=input +*.bndrun crlf=input +*.c crlf=input ident +*.conf crlf=input +*.cpp crlf=input ident +*.css crlf=input +*.ddf crlf=input +*.ee crlf=input +*.gradle crlf=input +*.groovy crlf=input +*.h crlf=input ident +*.html crlf=input ident +*.java crlf=input ident +*.js crlf=input +*.lib crlf=input +*.md crlf=input +*.MF crlf=input +*.mf crlf=input +*.perm crlf=input +*.php crlf=input +*.pl crlf=input +*.pom crlf=input +*.prefs crlf=input +*.properties crlf=input +*.py crlf=input +*.schema crlf=input +*.SF crlf=input +*.sh crlf=input +*.tcl crlf=input +*.txt crlf=input +*.xml crlf=input +*.xsd crlf=input ident +*.xsl crlf=input +*.xslt crlf=input +*.yml crlf=input +.classpath crlf=input +.project crlf=input +gradlew crlf=input +packageinfo crlf=input +Makefile crlf=input +README crlf=input +LICENSE crlf=input + +# No EOL translation +*.bat -crlf + +# Binary. No EOL translation, no diff +*.ico binary +*.jpeg binary +*.jpg binary +*.png binary +*.crt binary +*.pdf binary +*.dll binary +*.jar binary +*.jnilib binary +*.so binary +*.zip binary +*.doc binary +*.ppt binary +*.xls binary +*.odg binary +*.odp binary +*.ods binary +*.odt binary +*.otg binary +*.otp binary +*.ots binary +*.ott binary +*.key binary +*.numbers binary +*.pages binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..ca6309369f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.gradle/ +/generated/ +enroute.zip +.metadata diff --git a/.gradle-wrapper/gradle-wrapper.jar b/.gradle-wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 GIT binary patch literal 54708 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

    _x$+l{{cH8$W#CT literal 0 HcmV?d00001 diff --git a/.gradle-wrapper/gradle-wrapper.properties b/.gradle-wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..f16d26666b0 --- /dev/null +++ b/.gradle-wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000000..bd297790de3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +sudo: false + +language: java + +jdk: + - oraclejdk8 + +install: ./gradlew --version + +script: ./gradlew --continue diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000000..91265956166 --- /dev/null +++ b/build.gradle @@ -0,0 +1 @@ +// Available to customize the build diff --git a/cnf/.classpath b/cnf/.classpath new file mode 100644 index 00000000000..fb5011632c0 --- /dev/null +++ b/cnf/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/cnf/.gitignore b/cnf/.gitignore new file mode 100644 index 00000000000..bb3d1dda901 --- /dev/null +++ b/cnf/.gitignore @@ -0,0 +1,4 @@ +/generated/ +/bin/ +/cache/ +local/index.* diff --git a/cnf/.project b/cnf/.project new file mode 100644 index 00000000000..1397bbe6205 --- /dev/null +++ b/cnf/.project @@ -0,0 +1,11 @@ + + + cnf + + + + + + + + diff --git a/cnf/build.bnd b/cnf/build.bnd new file mode 100644 index 00000000000..5ea4bbbf5ef --- /dev/null +++ b/cnf/build.bnd @@ -0,0 +1,3 @@ +# +# This file is left open for you to define your macros +# diff --git a/cnf/central.xml b/cnf/central.xml new file mode 100644 index 00000000000..e96bb860e6e --- /dev/null +++ b/cnf/central.xml @@ -0,0 +1,19 @@ + + 4.0.0 + + local + central + 0.0.0 + + pom + + + + org.apache.felix + org.apache.felix.gogo.shell + 1.0.0 + + + + diff --git a/cnf/ext/enroute-distro.bnd b/cnf/ext/enroute-distro.bnd new file mode 100644 index 00000000000..f4b90501e14 --- /dev/null +++ b/cnf/ext/enroute-distro.bnd @@ -0,0 +1,49 @@ +# +# OSGi enRoute Default Distro +# +# This file defines the properties to be used in an enRoute workspace. +# + +-runfw: org.eclipse.osgi + +-plugin.9x.enroute.distro = \ + aQute.bnd.repository.maven.pom.provider.BndPomRepository; \ + snapshotUrls=https://oss.sonatype.org/content/groups/osgi; \ + releaseUrls=https://repo1.maven.org/maven2/; \ + revision=org.osgi:osgi.enroute.pom.distro:2.1.0-SNAPSHOT; \ + name=Distro; \ + location=${build}/cache/enroute-distro.xml + +-runblacklist.enroute: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.base.api)', \ + osgi.identity;filter:='(osgi.identity=osgi.cmpn)', \ + osgi.identity;filter:='(osgi.identity=osgi.core)', \ + osgi.identity;filter:='(osgi.identity=biz.aQute.junit)', \ + osgi.identity;filter:='(osgi.identity=biz.aQute.launcher)', \ + osgi.identity;filter:='(osgi.identity=biz.aQute.remote.launcher)' + + +# TODO check if still needed + +-runproperties.eqnx: \ + org.apache.felix.http.jettyEnabled=true, \ + org.apache.felix.http.whiteboardEnabled=true,\ + osgi.console=, \ + osgi.console.enable.builtin=false + +-runpath.eqnx: osgi.enroute.equinox.log.adapter +-runrequires.eqnx: \ + osgi.identity;filter:='(osgi.identity=org.apache.felix.log)' + + +-runsystempackages.eqnx: javax.script +-runsystemcapabilities.dflt: ${native_capability} + +debug-bundles: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.webconsole.xray.provider)',\ + osgi.implementation;filter:='(osgi.implementation=osgi.metatype)',\ + osgi.implementation;filter:='(osgi.implementation=osgi.log)',\ + osgi.implementation;filter:='(osgi.implementation=osgi.event)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)', \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.base.debug.provider)' diff --git a/cnf/ext/enroute.bnd b/cnf/ext/enroute.bnd new file mode 100644 index 00000000000..913a8e05a83 --- /dev/null +++ b/cnf/ext/enroute.bnd @@ -0,0 +1,43 @@ +# +# -ENROUTE- +# +# This file contains the setup for OSGi enRoute +# + + +javac.source: 1.8 +javac.target: 1.8 +-runee: JavaSE-1.8 + +Service-Component: * +-dsannotations: * +-metatypeannotations: * +-contract: * + + +test-cases: ${classes;NAMED;*Test} +-resolve.effective: resolve, active +-releaserepo: Release + +-plugin.4.Central: \ + aQute.bnd.repository.maven.pom.provider.BndPomRepository; \ + snapshotUrls=https://oss.sonatype.org/content/groups/osgi; \ + releaseUrls=https://repo1.maven.org/maven2/; \ + pom=${build}/central.xml; \ + name=Central; \ + location=${build}/cache/enroute-central.xml + +-plugin.6.Local: \ + aQute.bnd.deployer.repository.LocalIndexedRepo; \ + name = Local ; \ + pretty = true ; \ + local = ${build}/local + + + +-plugin.9.Release: \ +\ + aQute.bnd.deployer.repository.LocalIndexedRepo; \ + name = Release ; \ + pretty = true ; \ + local = ${build}/release diff --git a/cnf/release/index.xml b/cnf/release/index.xml new file mode 100644 index 00000000000..c89dffca14d --- /dev/null +++ b/cnf/release/index.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/cnf/release/index.xml.sha b/cnf/release/index.xml.sha new file mode 100644 index 00000000000..2771b155f61 --- /dev/null +++ b/cnf/release/index.xml.sha @@ -0,0 +1 @@ +b699f269b1871b7ef42e54b5ceb7529989f9474ffa4217c696e5ad333ac8936c \ No newline at end of file diff --git a/cnf/src/.gitignore b/cnf/src/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000000..b927f14f45c --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +bnd_version=3.4.0 diff --git a/gradlew b/gradlew new file mode 100644 index 00000000000..1c1e5df9de9 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/.gradle-wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000000..ec246509418 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\.gradle-wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/io.openems.backend.application/.classpath b/io.openems.backend.application/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.application/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.application/.gitignore b/io.openems.backend.application/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.application/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.application/.project b/io.openems.backend.application/.project new file mode 100644 index 00000000000..91009d66d47 --- /dev/null +++ b/io.openems.backend.application/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.application + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..2737fd0622a --- /dev/null +++ b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/application/BackendApp.java=UTF-8 +encoding//test/io/openems/backend/application/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/io.openems.backend.application.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.application/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.application/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.application/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd new file mode 100644 index 00000000000..abd42c1605b --- /dev/null +++ b/io.openems.backend.application/bnd.bnd @@ -0,0 +1,20 @@ +# +# io.openems.backend.application PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + + +Private-Package: \ + io.openems.backend.application + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1 + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +Bundle-Name: OpenEMS Backend \ No newline at end of file diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun new file mode 100644 index 00000000000..7bba90004d4 --- /dev/null +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -0,0 +1,20 @@ +# +# io.openems.backend.application LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.application.launch +JPM-Command: provider + + +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.application)' + +-runbundles: \ + io.openems.backend.application;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)' +-resolve: auto \ No newline at end of file diff --git a/io.openems.backend.application/readme.md b/io.openems.backend.application/readme.md new file mode 100644 index 00000000000..7d615385d9a --- /dev/null +++ b/io.openems.backend.application/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.application Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java new file mode 100644 index 00000000000..ab522b3a7e3 --- /dev/null +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -0,0 +1,20 @@ +package io.openems.backend.application; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; + +@Component() +public class BackendApp { + + @Activate + void activate() { + System.out.println("Activate"); + } + + @Deactivate + void deactivate() { + System.out.println("Deactivate"); + } + +} diff --git a/io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java b/io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java new file mode 100644 index 00000000000..b788316a58b --- /dev/null +++ b/io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java @@ -0,0 +1,26 @@ +package io.openems.backend.application; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.openems.backend.application.BackendApp; + +/* + * Example JUNit test case + * + */ + +public class ProviderImplTest { + + /* + * Example test method + */ + + @Test + public void simple() { + BackendApp impl = new BackendApp(); + assertNotNull(impl); + } + +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000000..be7b9e348ec --- /dev/null +++ b/settings.gradle @@ -0,0 +1,6 @@ +buildscript { + repositories { mavenCentral() } + dependencies { classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:${bnd_version}" } +} + +apply plugin: 'biz.aQute.bnd.workspace' From 55e5114be64dc983bc26fa6faded909915d3fad9 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 25 Jan 2018 14:14:33 +0100 Subject: [PATCH 010/156] Implement Metadata api --- .../org.eclipse.core.resources.prefs | 1 - io.openems.backend.application/bnd.bnd | 8 ++-- .../io.openems.backend.application.bndrun | 11 ++++-- .../backend/application/BackendApp.java | 13 +++++++ io.openems.backend.metadata.api/.classpath | 8 ++++ io.openems.backend.metadata.api/.gitignore | 3 ++ io.openems.backend.metadata.api/.project | 23 +++++++++++ .../org.eclipse.core.resources.prefs | 6 +++ .../.settings/org.eclipse.jdt.core.prefs | 11 ++++++ io.openems.backend.metadata.api/bnd.bnd | 23 +++++++++++ io.openems.backend.metadata.api/readme.md | 8 ++++ .../backend/metadata/api/MetadataService.java | 12 ++++++ .../backend/metadata/api/package-info.java | 2 + .../test/.gitignore | 0 io.openems.backend.metadata.odoo/.classpath | 8 ++++ io.openems.backend.metadata.odoo/.gitignore | 3 ++ io.openems.backend.metadata.odoo/.project | 23 +++++++++++ .../org.eclipse.core.resources.prefs | 7 ++++ .../.settings/org.eclipse.jdt.core.prefs | 11 ++++++ io.openems.backend.metadata.odoo/bnd.bnd | 23 +++++++++++ io.openems.backend.metadata.odoo/debug.bndrun | 13 +++++++ .../io.openems.backend.metadata.odoo.bndrun | 14 +++++++ io.openems.backend.metadata.odoo/readme.md | 8 ++++ .../backend/metadata/odoo/OdooImpl.java | 38 +++++++++++++++++++ .../metadata/odoo/ProviderImplTest.java | 26 +++++++++++++ io.openems.common/.classpath | 8 ++++ io.openems.common/.gitignore | 3 ++ io.openems.common/.project | 23 +++++++++++ .../org.eclipse.core.resources.prefs | 6 +++ .../.settings/org.eclipse.jdt.core.prefs | 11 ++++++ io.openems.common/bnd.bnd | 22 +++++++++++ io.openems.common/readme.md | 8 ++++ .../src/io/openems/common/ExampleService.java | 21 ++++++++++ .../io/openems/common/OpenemsException.java | 14 +++++++ .../src/io/openems/common/package-info.java | 2 + io.openems.common/test/.gitignore | 0 36 files changed, 414 insertions(+), 7 deletions(-) create mode 100644 io.openems.backend.metadata.api/.classpath create mode 100644 io.openems.backend.metadata.api/.gitignore create mode 100644 io.openems.backend.metadata.api/.project create mode 100644 io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.metadata.api/bnd.bnd create mode 100644 io.openems.backend.metadata.api/readme.md create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java create mode 100644 io.openems.backend.metadata.api/test/.gitignore create mode 100644 io.openems.backend.metadata.odoo/.classpath create mode 100644 io.openems.backend.metadata.odoo/.gitignore create mode 100644 io.openems.backend.metadata.odoo/.project create mode 100644 io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.metadata.odoo/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.metadata.odoo/bnd.bnd create mode 100644 io.openems.backend.metadata.odoo/debug.bndrun create mode 100644 io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun create mode 100644 io.openems.backend.metadata.odoo/readme.md create mode 100644 io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java create mode 100644 io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java create mode 100644 io.openems.common/.classpath create mode 100644 io.openems.common/.gitignore create mode 100644 io.openems.common/.project create mode 100644 io.openems.common/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.common/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.common/bnd.bnd create mode 100644 io.openems.common/readme.md create mode 100644 io.openems.common/src/io/openems/common/ExampleService.java create mode 100644 io.openems.common/src/io/openems/common/OpenemsException.java create mode 100644 io.openems.common/src/io/openems/common/package-info.java create mode 100644 io.openems.common/test/.gitignore diff --git a/backend/.settings/org.eclipse.core.resources.prefs b/backend/.settings/org.eclipse.core.resources.prefs index 2e41dd01076..03d9bd7c36d 100644 --- a/backend/.settings/org.eclipse.core.resources.prefs +++ b/backend/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,5 @@ eclipse.preferences.version=1 encoding//src/io/openems/impl/scheduler/SimpleScheduler.java=UTF-8 encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index abd42c1605b..8ec0af66d0b 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -10,11 +10,13 @@ Private-Package: \ -includeresource: {readme.md} --buildpath: \ - osgi.enroute.base.api;version=2.1 +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.common;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 -Bundle-Name: OpenEMS Backend \ No newline at end of file +Bundle-Name: OpenEMS Backend diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 7bba90004d4..182cef4c3bf 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -8,13 +8,18 @@ Bundle-SymbolicName: io.openems.backend.application.launch JPM-Command: provider --runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.application)' +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo)',\ + osgi.identity;filter:='(osgi.identity=io.openems.common)' +-resolve: auto -runbundles: \ io.openems.backend.application;version=snapshot,\ + io.openems.backend.metadata.odoo;version=snapshot,\ + io.openems.common;version=snapshot,\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)' --resolve: auto \ No newline at end of file + org.osgi.service.metatype;version='[1.3.0,1.3.1)' \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index ab522b3a7e3..a61af223846 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -3,13 +3,26 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; + +import io.openems.backend.metadata.api.MetadataService; +import io.openems.common.OpenemsException; @Component() public class BackendApp { + @Reference + MetadataService metadataService; + @Activate void activate() { System.out.println("Activate"); + System.out.println("Meta: " + this.metadataService); + try { + this.metadataService.getInfoWithSession(); + } catch (OpenemsException e) { + e.printStackTrace(); + } } @Deactivate diff --git a/io.openems.backend.metadata.api/.classpath b/io.openems.backend.metadata.api/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.metadata.api/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.metadata.api/.gitignore b/io.openems.backend.metadata.api/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.metadata.api/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.metadata.api/.project b/io.openems.backend.metadata.api/.project new file mode 100644 index 00000000000..ee22104f472 --- /dev/null +++ b/io.openems.backend.metadata.api/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.metadata.api + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..5eeaad5f1bb --- /dev/null +++ b/io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/metadata/api/MetadataService.java=UTF-8 +encoding//src/io/openems/backend/metadata/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd new file mode 100644 index 00000000000..a03a3a46f97 --- /dev/null +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -0,0 +1,23 @@ +# +# io.openems.backend.metadata.api DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} +Bundle-Description: Read and store Metadata + +Export-Package: \ + io.openems.backend.metadata.api + +Require-Capability: \ + compile-only + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.common;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.metadata.api/readme.md b/io.openems.backend.metadata.api/readme.md new file mode 100644 index 00000000000..4f114c8744d --- /dev/null +++ b/io.openems.backend.metadata.api/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.metadata.api + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java new file mode 100644 index 00000000000..d72428857c8 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -0,0 +1,12 @@ +package io.openems.backend.metadata.api; + +import org.osgi.annotation.versioning.ProviderType; + +import io.openems.common.OpenemsException; + +@ProviderType +public interface MetadataService { + + public void getInfoWithSession() throws OpenemsException; + +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java new file mode 100644 index 00000000000..fde179da302 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.metadata.api; diff --git a/io.openems.backend.metadata.api/test/.gitignore b/io.openems.backend.metadata.api/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.backend.metadata.odoo/.classpath b/io.openems.backend.metadata.odoo/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.metadata.odoo/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.metadata.odoo/.gitignore b/io.openems.backend.metadata.odoo/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.metadata.odoo/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.metadata.odoo/.project b/io.openems.backend.metadata.odoo/.project new file mode 100644 index 00000000000..57448a32928 --- /dev/null +++ b/io.openems.backend.metadata.odoo/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.metadata.odoo + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..662e7d0f817 --- /dev/null +++ b/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/metadata/odoo/OdooImpl.java=UTF-8 +encoding//test/io/openems/backend/metadata/odoo/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.metadata.odoo.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.metadata.odoo/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.metadata.odoo/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.metadata.odoo/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.metadata.odoo/bnd.bnd b/io.openems.backend.metadata.odoo/bnd.bnd new file mode 100644 index 00000000000..c396ceef70b --- /dev/null +++ b/io.openems.backend.metadata.odoo/bnd.bnd @@ -0,0 +1,23 @@ +# +# io.openems.backend.metadata.odoo PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + + +Private-Package: \ + io.openems.backend.metadata.odoo + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.common;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.api)' +Export-Package: io.openems.backend.metadata.api \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo/debug.bndrun b/io.openems.backend.metadata.odoo/debug.bndrun new file mode 100644 index 00000000000..33e2e1c5079 --- /dev/null +++ b/io.openems.backend.metadata.odoo/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.metadata.odoo DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.metadata.odoo.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun b/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun new file mode 100644 index 00000000000..ec9e1ea520b --- /dev/null +++ b/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun @@ -0,0 +1,14 @@ +# +# io.openems.backend.metadata.odoo LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.metadata.odoo.launch +JPM-Command: provider + + +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo.provider)' + +-runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.metadata.odoo/readme.md b/io.openems.backend.metadata.odoo/readme.md new file mode 100644 index 00000000000..0b11c20f211 --- /dev/null +++ b/io.openems.backend.metadata.odoo/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.metadata.odoo Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java new file mode 100644 index 00000000000..815d126b292 --- /dev/null +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java @@ -0,0 +1,38 @@ +package io.openems.backend.metadata.odoo; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.backend.metadata.api.MetadataService; +import io.openems.common.OpenemsException; + +import org.osgi.service.metatype.annotations.Designate; + +@Designate( ocd=OdooImpl.Config.class, factory=true) +@Component(name="io.openems.backend.metadata.odoo") +public class OdooImpl implements MetadataService { + + @ObjectClassDefinition + @interface Config { + String name() default "World"; + } + + private String name; + + @Activate + void activate(Config config) { + this.name = config.name(); + } + + @Deactivate + void deactivate() { + } + + @Override + public void getInfoWithSession() throws OpenemsException { + System.out.println("Just saying "); + } + +} diff --git a/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java b/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java new file mode 100644 index 00000000000..f10ee38dcdf --- /dev/null +++ b/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java @@ -0,0 +1,26 @@ +package io.openems.backend.metadata.odoo; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.openems.backend.metadata.odoo.OdooImpl; + +/* + * Example JUNit test case + * + */ + +public class ProviderImplTest { + + /* + * Example test method + */ + + @Test + public void simple() { + OdooImpl impl = new OdooImpl(); + assertNotNull(impl); + } + +} diff --git a/io.openems.common/.classpath b/io.openems.common/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.common/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.common/.gitignore b/io.openems.common/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.common/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.common/.project b/io.openems.common/.project new file mode 100644 index 00000000000..adef5263dd4 --- /dev/null +++ b/io.openems.common/.project @@ -0,0 +1,23 @@ + + + io.openems.common + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.common/.settings/org.eclipse.core.resources.prefs b/io.openems.common/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..1ba98e2695e --- /dev/null +++ b/io.openems.common/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/common/ExampleService.java=UTF-8 +encoding//src/io/openems/common/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.common/.settings/org.eclipse.jdt.core.prefs b/io.openems.common/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.common/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd new file mode 100644 index 00000000000..71a501522e5 --- /dev/null +++ b/io.openems.common/bnd.bnd @@ -0,0 +1,22 @@ +# +# io.openems.common DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} +Bundle-Description: \ + This project contains a complete example with API, provider, and JUnit test code. \ + \ + ${warning;Please update this Bundle-Description in bnd.bnd} + +Export-Package: \ + io.openems.common + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1 + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.common/readme.md b/io.openems.common/readme.md new file mode 100644 index 00000000000..4b7feb18524 --- /dev/null +++ b/io.openems.common/readme.md @@ -0,0 +1,8 @@ +# io.openems.common + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.common/src/io/openems/common/ExampleService.java b/io.openems.common/src/io/openems/common/ExampleService.java new file mode 100644 index 00000000000..e8a7672506b --- /dev/null +++ b/io.openems.common/src/io/openems/common/ExampleService.java @@ -0,0 +1,21 @@ +package io.openems.common; + +import org.osgi.annotation.versioning.ProviderType; + +/** + * This is an example OSGi enRoute bundle that has a component that implements an + * API. + */ + +@ProviderType +public interface ExampleService { + + /** + * The interface is a minimal method. + * + * @param message the message to say + * @return true if the message could be spoken + */ + boolean say(String message); + +} diff --git a/io.openems.common/src/io/openems/common/OpenemsException.java b/io.openems.common/src/io/openems/common/OpenemsException.java new file mode 100644 index 00000000000..4b361f18120 --- /dev/null +++ b/io.openems.common/src/io/openems/common/OpenemsException.java @@ -0,0 +1,14 @@ +package io.openems.common; + +public class OpenemsException extends Exception { + + private static final long serialVersionUID = 1L; + + public OpenemsException(String message) { + super(message); + } + + public OpenemsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/io.openems.common/src/io/openems/common/package-info.java b/io.openems.common/src/io/openems/common/package-info.java new file mode 100644 index 00000000000..23c6837cfd8 --- /dev/null +++ b/io.openems.common/src/io/openems/common/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common; diff --git a/io.openems.common/test/.gitignore b/io.openems.common/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d From f080bdad0904be27e0bd0e27e3f3bcbc2ec0ddb3 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 25 Jan 2018 17:13:19 +0100 Subject: [PATCH 011/156] Move OdooSingleton --- .../backend/metadata/odoo/OdooSingleton.java | 140 -------------- .../backend/metadata/odoo/OdooImpl.java | 178 ++++++++++++++---- 2 files changed, 140 insertions(+), 178 deletions(-) delete mode 100644 backend/src/main/java/io/openems/backend/metadata/odoo/OdooSingleton.java diff --git a/backend/src/main/java/io/openems/backend/metadata/odoo/OdooSingleton.java b/backend/src/main/java/io/openems/backend/metadata/odoo/OdooSingleton.java deleted file mode 100644 index 45215fb0ec1..00000000000 --- a/backend/src/main/java/io/openems/backend/metadata/odoo/OdooSingleton.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.openems.backend.metadata.odoo; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; - -import org.apache.xmlrpc.XmlRpcException; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.odoojava.api.OdooApiException; -import com.odoojava.api.Session; - -import io.openems.backend.browserwebsocket.session.BrowserSession; -import io.openems.backend.browserwebsocket.session.BrowserSessionData; -import io.openems.backend.metadata.api.MetadataSingleton; -import io.openems.backend.metadata.api.device.MetadataDeviceModel; -import io.openems.backend.metadata.odoo.device.OdooDeviceModel; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.session.SessionData; -import io.openems.common.types.DeviceImpl; -import io.openems.common.utils.JsonUtils; - -public class OdooSingleton implements MetadataSingleton { - private Session session; - private MetadataDeviceModel deviceModel; - private final String url; - - public OdooSingleton(String url, int port, String database, String username, String password) - throws OpenemsException { - this.session = new Session(url, port, database, username, password); - this.connect(); - try { - this.deviceModel = new OdooDeviceModel(this.session); - } catch (XmlRpcException | OdooApiException e) { - throw new OpenemsException("Initializing OdooDeviceModel failed: " + e.getMessage()); - } - this.url = "http://" + url + ":" + port; - } - - private void connect() throws OpenemsException { - try { - session.startSession(); - } catch (Exception e) { - throw new OpenemsException("Odoo connection failed: " + e.getMessage()); - } - } - - @Override - public MetadataDeviceModel getDeviceModel() { - return deviceModel; - } - - /** - * Tries to authenticate at the Odoo server using a sessionId from a cookie. Updates the Session object accordingly. - * - * @param sessionId - * @return - * @throws OpenemsException - */ - @Override - public void getInfoWithSession(BrowserSession session) throws OpenemsException { - HttpURLConnection connection = null; - try { - // get session_id from Session - SessionData sessionData = session.getData(); - if (!(sessionData instanceof BrowserSessionData)) { - throw new OpenemsException("Session is of wrong type."); - } - BrowserSessionData data = (BrowserSessionData) sessionData; - if (!(data.getOdooSessionId().isPresent())) { - throw new OpenemsException("Session-ID is missing."); - } - String sessionId = data.getOdooSessionId().get(); - - // send request to Odoo - String charset = "US-ASCII"; - String query = String.format("session_id=%s", URLEncoder.encode(sessionId, charset)); - connection = (HttpURLConnection) new URL(this.url + "/openems_backend/info?" + query).openConnection(); - connection.setConnectTimeout(5000);// 5 secs - connection.setReadTimeout(5000);// 5 secs - connection.setRequestProperty("Accept-Charset", charset); - connection.setRequestMethod("POST"); - connection.setDoOutput(true); - connection.setRequestProperty("Content-Type", "application/json"); - - OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); - out.write("{}"); - out.flush(); - out.close(); - - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String line = null; - while ((line = br.readLine()) != null) { - JsonObject j = (new JsonParser()).parse(line).getAsJsonObject(); - if (j.has("error")) { - JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); - String errorMessage = JsonUtils.getAsString(jError, "message"); - throw new OpenemsException("Odoo replied with error: " + errorMessage); - } - - if (j.has("result")) { - // parse the result - JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); - JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); - data.setUserId(JsonUtils.getAsInt(jUser, "id")); - data.setUserName(JsonUtils.getAsString(jUser, "name")); - JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); - LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); - for (JsonElement jDevice : jDevices) { - String name = JsonUtils.getAsString(jDevice, "name"); - deviceMap.put(name, new DeviceImpl( // - name, // - JsonUtils.getAsString(jDevice, "comment"), // - JsonUtils.getAsString(jDevice, "producttype"), // - JsonUtils.getAsString(jDevice, "role"))); - } - data.setDevices(deviceMap); - return; - } - } - } catch (IOException e) { - throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); - } finally { - if (connection != null) { - connection.disconnect(); - } - } - throw new OpenemsException("No result from Odoo"); - } -} diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java index 815d126b292..45215fb0ec1 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java @@ -1,38 +1,140 @@ -package io.openems.backend.metadata.odoo; - -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; - -import io.openems.backend.metadata.api.MetadataService; -import io.openems.common.OpenemsException; - -import org.osgi.service.metatype.annotations.Designate; - -@Designate( ocd=OdooImpl.Config.class, factory=true) -@Component(name="io.openems.backend.metadata.odoo") -public class OdooImpl implements MetadataService { - - @ObjectClassDefinition - @interface Config { - String name() default "World"; - } - - private String name; - - @Activate - void activate(Config config) { - this.name = config.name(); - } - - @Deactivate - void deactivate() { - } - - @Override - public void getInfoWithSession() throws OpenemsException { - System.out.println("Just saying "); - } - -} +package io.openems.backend.metadata.odoo; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; + +import org.apache.xmlrpc.XmlRpcException; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.odoojava.api.OdooApiException; +import com.odoojava.api.Session; + +import io.openems.backend.browserwebsocket.session.BrowserSession; +import io.openems.backend.browserwebsocket.session.BrowserSessionData; +import io.openems.backend.metadata.api.MetadataSingleton; +import io.openems.backend.metadata.api.device.MetadataDeviceModel; +import io.openems.backend.metadata.odoo.device.OdooDeviceModel; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.SessionData; +import io.openems.common.types.DeviceImpl; +import io.openems.common.utils.JsonUtils; + +public class OdooSingleton implements MetadataSingleton { + private Session session; + private MetadataDeviceModel deviceModel; + private final String url; + + public OdooSingleton(String url, int port, String database, String username, String password) + throws OpenemsException { + this.session = new Session(url, port, database, username, password); + this.connect(); + try { + this.deviceModel = new OdooDeviceModel(this.session); + } catch (XmlRpcException | OdooApiException e) { + throw new OpenemsException("Initializing OdooDeviceModel failed: " + e.getMessage()); + } + this.url = "http://" + url + ":" + port; + } + + private void connect() throws OpenemsException { + try { + session.startSession(); + } catch (Exception e) { + throw new OpenemsException("Odoo connection failed: " + e.getMessage()); + } + } + + @Override + public MetadataDeviceModel getDeviceModel() { + return deviceModel; + } + + /** + * Tries to authenticate at the Odoo server using a sessionId from a cookie. Updates the Session object accordingly. + * + * @param sessionId + * @return + * @throws OpenemsException + */ + @Override + public void getInfoWithSession(BrowserSession session) throws OpenemsException { + HttpURLConnection connection = null; + try { + // get session_id from Session + SessionData sessionData = session.getData(); + if (!(sessionData instanceof BrowserSessionData)) { + throw new OpenemsException("Session is of wrong type."); + } + BrowserSessionData data = (BrowserSessionData) sessionData; + if (!(data.getOdooSessionId().isPresent())) { + throw new OpenemsException("Session-ID is missing."); + } + String sessionId = data.getOdooSessionId().get(); + + // send request to Odoo + String charset = "US-ASCII"; + String query = String.format("session_id=%s", URLEncoder.encode(sessionId, charset)); + connection = (HttpURLConnection) new URL(this.url + "/openems_backend/info?" + query).openConnection(); + connection.setConnectTimeout(5000);// 5 secs + connection.setReadTimeout(5000);// 5 secs + connection.setRequestProperty("Accept-Charset", charset); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json"); + + OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); + out.write("{}"); + out.flush(); + out.close(); + + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line = null; + while ((line = br.readLine()) != null) { + JsonObject j = (new JsonParser()).parse(line).getAsJsonObject(); + if (j.has("error")) { + JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); + String errorMessage = JsonUtils.getAsString(jError, "message"); + throw new OpenemsException("Odoo replied with error: " + errorMessage); + } + + if (j.has("result")) { + // parse the result + JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); + JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); + data.setUserId(JsonUtils.getAsInt(jUser, "id")); + data.setUserName(JsonUtils.getAsString(jUser, "name")); + JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); + LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); + for (JsonElement jDevice : jDevices) { + String name = JsonUtils.getAsString(jDevice, "name"); + deviceMap.put(name, new DeviceImpl( // + name, // + JsonUtils.getAsString(jDevice, "comment"), // + JsonUtils.getAsString(jDevice, "producttype"), // + JsonUtils.getAsString(jDevice, "role"))); + } + data.setDevices(deviceMap); + return; + } + } + } catch (IOException e) { + throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + throw new OpenemsException("No result from Odoo"); + } +} From 01683eba53de24a3aa5c1f34da9952b05cf4303f Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 25 Jan 2018 17:23:43 +0100 Subject: [PATCH 012/156] Implement connection to Odoo --- cnf/central.xml | 20 ++ .../io.openems.backend.application.bndrun | 11 +- io.openems.backend.metadata.odoo/bnd.bnd | 8 +- io.openems.backend.metadata.odoo/debug.bndrun | 4 + .../io.openems.backend.metadata.odoo.bndrun | 3 +- .../backend/metadata/odoo/OdooImpl.java | 281 +++++++++--------- 6 files changed, 180 insertions(+), 147 deletions(-) diff --git a/cnf/central.xml b/cnf/central.xml index e96bb860e6e..95e2d016e6a 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -9,11 +9,31 @@ pom + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.xmlrpc-client + 3.1.3_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.commons-httpclient + 3.1_7 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.ws-commons-util + 1.0.2_2 + org.apache.felix org.apache.felix.gogo.shell 1.0.0 + + com.google.code.gson + gson + 2.8.2 + diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 182cef4c3bf..fb692258320 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -11,15 +11,22 @@ JPM-Command: provider -runrequires: \ osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo)',\ - osgi.identity;filter:='(osgi.identity=io.openems.common)' + osgi.identity;filter:='(osgi.identity=io.openems.common)',\ + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.commons-httpclient)',\ + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.ws-commons-util)',\ + osgi.identity;filter:='(osgi.identity=com.google.gson)' -resolve: auto -runbundles: \ io.openems.backend.application;version=snapshot,\ io.openems.backend.metadata.odoo;version=snapshot,\ io.openems.common;version=snapshot,\ + org.apache.commons.codec;version='[1.10.0,1.10.1)',\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ + org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)' \ No newline at end of file + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + com.google.gson;version='[2.8.2,2.8.3)' \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo/bnd.bnd b/io.openems.backend.metadata.odoo/bnd.bnd index c396ceef70b..e5cca0e5409 100644 --- a/io.openems.backend.metadata.odoo/bnd.bnd +++ b/io.openems.backend.metadata.odoo/bnd.bnd @@ -13,11 +13,13 @@ Private-Package: \ -buildpath: \ osgi.enroute.base.api;version=2.1,\ io.openems.backend.metadata.api;version=latest,\ - io.openems.common;version=latest + io.openems.common;version=latest,\ + org.apache.servicemix.bundles.xmlrpc-client,\ + com.google.gson -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 --runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.api)' -Export-Package: io.openems.backend.metadata.api \ No newline at end of file +Export-Package: io.openems.backend.metadata.api +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo/debug.bndrun b/io.openems.backend.metadata.odoo/debug.bndrun index 33e2e1c5079..d2f4021ac1b 100644 --- a/io.openems.backend.metadata.odoo/debug.bndrun +++ b/io.openems.backend.metadata.odoo/debug.bndrun @@ -11,3 +11,7 @@ -runbundles: \ ${error;Resolve first} +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.api)',\ + osgi.identity;filter:='(osgi.identity=org.apache.ws.commons.util:ws-commons-util)' \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun b/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun index ec9e1ea520b..a3f971763ee 100644 --- a/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun +++ b/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun @@ -8,7 +8,6 @@ Bundle-SymbolicName: io.openems.backend.metadata.odoo.launch JPM-Command: provider --runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo.provider)' +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo.provider)' -runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java index 45215fb0ec1..300644e7c8f 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java @@ -1,140 +1,141 @@ -package io.openems.backend.metadata.odoo; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; - -import org.apache.xmlrpc.XmlRpcException; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.odoojava.api.OdooApiException; -import com.odoojava.api.Session; - -import io.openems.backend.browserwebsocket.session.BrowserSession; -import io.openems.backend.browserwebsocket.session.BrowserSessionData; -import io.openems.backend.metadata.api.MetadataSingleton; -import io.openems.backend.metadata.api.device.MetadataDeviceModel; -import io.openems.backend.metadata.odoo.device.OdooDeviceModel; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.session.SessionData; -import io.openems.common.types.DeviceImpl; -import io.openems.common.utils.JsonUtils; - -public class OdooSingleton implements MetadataSingleton { - private Session session; - private MetadataDeviceModel deviceModel; - private final String url; - - public OdooSingleton(String url, int port, String database, String username, String password) - throws OpenemsException { - this.session = new Session(url, port, database, username, password); - this.connect(); - try { - this.deviceModel = new OdooDeviceModel(this.session); - } catch (XmlRpcException | OdooApiException e) { - throw new OpenemsException("Initializing OdooDeviceModel failed: " + e.getMessage()); - } - this.url = "http://" + url + ":" + port; - } - - private void connect() throws OpenemsException { - try { - session.startSession(); - } catch (Exception e) { - throw new OpenemsException("Odoo connection failed: " + e.getMessage()); - } - } - - @Override - public MetadataDeviceModel getDeviceModel() { - return deviceModel; - } - - /** - * Tries to authenticate at the Odoo server using a sessionId from a cookie. Updates the Session object accordingly. - * - * @param sessionId - * @return - * @throws OpenemsException - */ - @Override - public void getInfoWithSession(BrowserSession session) throws OpenemsException { - HttpURLConnection connection = null; - try { - // get session_id from Session - SessionData sessionData = session.getData(); - if (!(sessionData instanceof BrowserSessionData)) { - throw new OpenemsException("Session is of wrong type."); - } - BrowserSessionData data = (BrowserSessionData) sessionData; - if (!(data.getOdooSessionId().isPresent())) { - throw new OpenemsException("Session-ID is missing."); - } - String sessionId = data.getOdooSessionId().get(); - - // send request to Odoo - String charset = "US-ASCII"; - String query = String.format("session_id=%s", URLEncoder.encode(sessionId, charset)); - connection = (HttpURLConnection) new URL(this.url + "/openems_backend/info?" + query).openConnection(); - connection.setConnectTimeout(5000);// 5 secs - connection.setReadTimeout(5000);// 5 secs - connection.setRequestProperty("Accept-Charset", charset); - connection.setRequestMethod("POST"); - connection.setDoOutput(true); - connection.setRequestProperty("Content-Type", "application/json"); - - OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); - out.write("{}"); - out.flush(); - out.close(); - - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String line = null; - while ((line = br.readLine()) != null) { - JsonObject j = (new JsonParser()).parse(line).getAsJsonObject(); - if (j.has("error")) { - JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); - String errorMessage = JsonUtils.getAsString(jError, "message"); - throw new OpenemsException("Odoo replied with error: " + errorMessage); - } - - if (j.has("result")) { - // parse the result - JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); - JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); - data.setUserId(JsonUtils.getAsInt(jUser, "id")); - data.setUserName(JsonUtils.getAsString(jUser, "name")); - JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); - LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); - for (JsonElement jDevice : jDevices) { - String name = JsonUtils.getAsString(jDevice, "name"); - deviceMap.put(name, new DeviceImpl( // - name, // - JsonUtils.getAsString(jDevice, "comment"), // - JsonUtils.getAsString(jDevice, "producttype"), // - JsonUtils.getAsString(jDevice, "role"))); - } - data.setDevices(deviceMap); - return; - } - } - } catch (IOException e) { - throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); - } finally { - if (connection != null) { - connection.disconnect(); - } - } - throw new OpenemsException("No result from Odoo"); - } -} +package io.openems.backend.metadata.odoo; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.backend.metadata.api.MetadataService; +import io.openems.common.OpenemsException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +@Designate(ocd = OdooImpl.Config.class, factory = true) +@Component(name = "io.openems.backend.metadata.odoo") +public class OdooImpl implements MetadataService { + + @ObjectClassDefinition + @interface Config { + String database(); + int uid(); + String password(); + String url() default "https://www1.fenecon.de"; + } + + private String url; + private String database; + private int uid; + private String password; + + @Activate + void activate(Config config) { + this.url = config.url(); + this.database = config.database(); + this.uid = config.uid(); + this.password = config.password(); + } + + @Deactivate + void deactivate() { + } + + // private MetadataDeviceModel deviceModel; + + // @Override + // public MetadataDeviceModel getDeviceModel() { + // return deviceModel; + // } + + /** + * Tries to authenticate at the Odoo server using a sessionId from a cookie. Updates the Session object accordingly. + * + * @param sessionId + * @return + * @throws OpenemsException + */ + @Override + // public void getInfoWithSession(BrowserSession session) throws OpenemsException { + public void getInfoWithSession() throws OpenemsException { + HttpURLConnection connection = null; + try { + // get session_id from Session + // SessionData sessionData = session.getData(); + // if (!(sessionData instanceof BrowserSessionData)) { + // throw new OpenemsException("Session is of wrong type."); + // } + // BrowserSessionData data = (BrowserSessionData) sessionData; + // if (!(data.getOdooSessionId().isPresent())) { + // throw new OpenemsException("Session-ID is missing."); + // } + // String sessionId = data.getOdooSessionId().get(); + String sessionId = ""; + + // send request to Odoo + String charset = "US-ASCII"; + String query = String.format("session_id=%s", URLEncoder.encode(sessionId, charset)); + connection = (HttpURLConnection) new URL(this.url + "/openems_backend/info?" + query).openConnection(); + connection.setConnectTimeout(5000);// 5 secs + connection.setReadTimeout(5000);// 5 secs + connection.setRequestProperty("Accept-Charset", charset); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json"); + + OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); + out.write("{}"); + out.flush(); + out.close(); + + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line = null; + while ((line = br.readLine()) != null) { + System.out.println(line); + JsonObject j = (new JsonParser()).parse(line).getAsJsonObject(); +// if (j.has("error")) { +// JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); +// String errorMessage = JsonUtils.getAsString(jError, "message"); +// throw new OpenemsException("Odoo replied with error: " + errorMessage); +// } + +// if (j.has("result")) { +// // parse the result +// JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); +// JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); +// data.setUserId(JsonUtils.getAsInt(jUser, "id")); +// data.setUserName(JsonUtils.getAsString(jUser, "name")); +// JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); +// LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); +// for (JsonElement jDevice : jDevices) { +// String name = JsonUtils.getAsString(jDevice, "name"); +// deviceMap.put(name, new DeviceImpl( // +// name, // +// JsonUtils.getAsString(jDevice, "comment"), // +// JsonUtils.getAsString(jDevice, "producttype"), // +// JsonUtils.getAsString(jDevice, "role"))); +// } +// data.setDevices(deviceMap); +// return; +// } + } + } catch (IOException e) { + throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + throw new OpenemsException("No result from Odoo"); + } +} From 77563508784d2581cc7b03f61d8b159589e361cd Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 26 Jan 2018 09:09:45 +0100 Subject: [PATCH 013/156] Working OdooImpl.getInfoWithSession() --- cnf/central.xml | 33 +- .../common/exceptions/OpenemsException.java | 13 - .../io.openems.backend.application.bndrun | 24 +- .../backend/application/BackendApp.java | 2 +- io.openems.backend.common/.classpath | 8 + io.openems.backend.common/.gitignore | 3 + io.openems.backend.common/.project | 23 + .../org.eclipse.core.resources.prefs | 4 + .../.settings/org.eclipse.jdt.core.prefs | 11 + io.openems.backend.common/bnd.bnd | 18 + io.openems.backend.common/readme.md | 8 + .../openems/backend}/common/types/Device.java | 42 +- .../backend}/common/types/DeviceImpl.java | 2 +- .../backend/common/types/package-info.java | 2 + io.openems.backend.common/test/.gitignore | 0 io.openems.backend.metadata.api/bnd.bnd | 3 - .../backend/metadata/api/MetadataService.java | 2 +- io.openems.backend.metadata.odoo/bnd.bnd | 4 +- .../backend/metadata/odoo/OdooImpl.java | 57 +- .../org.eclipse.core.resources.prefs | 2 - io.openems.common/bnd.bnd | 11 +- .../src/io/openems/common/ExampleService.java | 21 - .../exceptions/NotImplementedException.java | 0 .../{ => exceptions}/OpenemsException.java | 2 +- .../common/exceptions/package-info.java | 2 + .../src/io/openems/common/session/Role.java | 46 +- .../openems/common/session/package-info.java | 2 + .../io/openems/common/utils/JsonUtils.java | 674 +++++++++--------- .../common/{ => utils}/package-info.java | 2 +- 29 files changed, 550 insertions(+), 471 deletions(-) delete mode 100644 common/src/io/openems/common/exceptions/OpenemsException.java create mode 100644 io.openems.backend.common/.classpath create mode 100644 io.openems.backend.common/.gitignore create mode 100644 io.openems.backend.common/.project create mode 100644 io.openems.backend.common/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.common/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.common/bnd.bnd create mode 100644 io.openems.backend.common/readme.md rename {common/src/io/openems => io.openems.backend.common/src/io/openems/backend}/common/types/Device.java (90%) rename {common/src/io/openems => io.openems.backend.common/src/io/openems/backend}/common/types/DeviceImpl.java (96%) create mode 100644 io.openems.backend.common/src/io/openems/backend/common/types/package-info.java create mode 100644 io.openems.backend.common/test/.gitignore delete mode 100644 io.openems.common/src/io/openems/common/ExampleService.java rename {common => io.openems.common}/src/io/openems/common/exceptions/NotImplementedException.java (100%) rename io.openems.common/src/io/openems/common/{ => exceptions}/OpenemsException.java (87%) create mode 100644 io.openems.common/src/io/openems/common/exceptions/package-info.java rename {common => io.openems.common}/src/io/openems/common/session/Role.java (94%) create mode 100644 io.openems.common/src/io/openems/common/session/package-info.java rename {common => io.openems.common}/src/io/openems/common/utils/JsonUtils.java (96%) rename io.openems.common/src/io/openems/common/{ => utils}/package-info.java (59%) diff --git a/cnf/central.xml b/cnf/central.xml index 95e2d016e6a..060f2446da0 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -9,10 +9,22 @@ pom + - org.apache.servicemix.bundles - org.apache.servicemix.bundles.xmlrpc-client - 3.1.3_1 + com.google.code.gson + gson + 2.8.2 + + + com.google.guava + guava + 23.6-jre + + + + org.apache.felix + org.apache.felix.gogo.shell + 1.0.0 org.apache.servicemix.bundles @@ -21,18 +33,13 @@ org.apache.servicemix.bundles - org.apache.servicemix.bundles.ws-commons-util - 1.0.2_2 - - - org.apache.felix - org.apache.felix.gogo.shell - 1.0.0 + org.apache.servicemix.bundles.xmlrpc-client + 3.1.3_1 - com.google.code.gson - gson - 2.8.2 + org.apache.servicemix.bundles + org.apache.servicemix.bundles.ws-commons-util + 1.0.2_2 diff --git a/common/src/io/openems/common/exceptions/OpenemsException.java b/common/src/io/openems/common/exceptions/OpenemsException.java deleted file mode 100644 index aabebeee623..00000000000 --- a/common/src/io/openems/common/exceptions/OpenemsException.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.openems.common.exceptions; - -public class OpenemsException extends Exception { - private static final long serialVersionUID = 5015013132334439401L; - - public OpenemsException(String message) { - super(message); - } - - public OpenemsException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index fb692258320..026f83972d1 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -14,19 +14,39 @@ JPM-Command: provider osgi.identity;filter:='(osgi.identity=io.openems.common)',\ osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.commons-httpclient)',\ osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.ws-commons-util)',\ - osgi.identity;filter:='(osgi.identity=com.google.gson)' + osgi.identity;filter:='(osgi.identity=com.google.gson)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.webconsole)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.webconsole.xray.provider)',\ + osgi.identity;filter:='(osgi.identity=com.google.guava)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.common)' -resolve: auto -runbundles: \ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[23.6.0,23.6.1)',\ io.openems.backend.application;version=snapshot,\ + io.openems.backend.common;version=snapshot,\ io.openems.backend.metadata.odoo;version=snapshot,\ io.openems.common;version=snapshot,\ + json;version='[20160212.0.0,20160212.0.1)',\ org.apache.commons.codec;version='[1.10.0,1.10.1)',\ + org.apache.commons.fileupload;version='[1.3.2,1.3.3)',\ + org.apache.commons.io;version='[2.5.0,2.5.1)',\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\ + org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.apache.felix.webconsole;version='[4.2.16,4.2.17)',\ org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.compendium;version='[4.1.0,4.1.1)',\ + org.osgi.service.event;version='[1.3.1,1.3.2)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - com.google.gson;version='[2.8.2,2.8.3)' \ No newline at end of file + osgi.enroute.bostock.d3.webresource;version='[3.5.6,3.5.7)',\ + osgi.enroute.executor.simple.provider;version='[2.1.0,2.1.1)',\ + osgi.enroute.logger.simple.provider;version='[2.1.0,2.1.1)',\ + osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)',\ + osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)' \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index a61af223846..02abb87cb3b 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -6,7 +6,7 @@ import org.osgi.service.component.annotations.Reference; import io.openems.backend.metadata.api.MetadataService; -import io.openems.common.OpenemsException; +import io.openems.common.exceptions.OpenemsException; @Component() public class BackendApp { diff --git a/io.openems.backend.common/.classpath b/io.openems.backend.common/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.common/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.common/.gitignore b/io.openems.backend.common/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.common/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.common/.project b/io.openems.backend.common/.project new file mode 100644 index 00000000000..737f106c27d --- /dev/null +++ b/io.openems.backend.common/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.common + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..b7701f5d0cb --- /dev/null +++ b/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.common/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.common/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.common/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.common/bnd.bnd b/io.openems.backend.common/bnd.bnd new file mode 100644 index 00000000000..956109105b1 --- /dev/null +++ b/io.openems.backend.common/bnd.bnd @@ -0,0 +1,18 @@ +# +# io.openems.backend.common DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: io.openems.backend.common.types + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.common;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.common/readme.md b/io.openems.backend.common/readme.md new file mode 100644 index 00000000000..1e32cfdd5c8 --- /dev/null +++ b/io.openems.backend.common/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.common + +${Bundle-Description} + +## Example + +## References + diff --git a/common/src/io/openems/common/types/Device.java b/io.openems.backend.common/src/io/openems/backend/common/types/Device.java similarity index 90% rename from common/src/io/openems/common/types/Device.java rename to io.openems.backend.common/src/io/openems/backend/common/types/Device.java index ef86a5528c4..3e16beb87ed 100644 --- a/common/src/io/openems/common/types/Device.java +++ b/io.openems.backend.common/src/io/openems/backend/common/types/Device.java @@ -1,21 +1,21 @@ -package io.openems.common.types; - -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public interface Device { - - final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); - - public Optional getIdOpt(); - - public static Optional parseNumberFromName(String name) { - Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); - if (matcher.find()) { - String nameNumberString = matcher.group(1); - return Optional.ofNullable(Integer.parseInt(nameNumberString)); - } - return Optional.empty(); - } -} +package io.openems.backend.common.types; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public interface Device { + + final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); + + public Optional getIdOpt(); + + public static Optional parseNumberFromName(String name) { + Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); + if (matcher.find()) { + String nameNumberString = matcher.group(1); + return Optional.ofNullable(Integer.parseInt(nameNumberString)); + } + return Optional.empty(); + } +} diff --git a/common/src/io/openems/common/types/DeviceImpl.java b/io.openems.backend.common/src/io/openems/backend/common/types/DeviceImpl.java similarity index 96% rename from common/src/io/openems/common/types/DeviceImpl.java rename to io.openems.backend.common/src/io/openems/backend/common/types/DeviceImpl.java index 124d8e721a9..89b933e0aee 100644 --- a/common/src/io/openems/common/types/DeviceImpl.java +++ b/io.openems.backend.common/src/io/openems/backend/common/types/DeviceImpl.java @@ -1,4 +1,4 @@ -package io.openems.common.types; +package io.openems.backend.common.types; import java.util.Optional; diff --git a/io.openems.backend.common/src/io/openems/backend/common/types/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/types/package-info.java new file mode 100644 index 00000000000..f6e1f853daa --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/types/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.common.types; diff --git a/io.openems.backend.common/test/.gitignore b/io.openems.backend.common/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd index a03a3a46f97..20cf5875684 100644 --- a/io.openems.backend.metadata.api/bnd.bnd +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -8,9 +8,6 @@ Bundle-Description: Read and store Metadata Export-Package: \ io.openems.backend.metadata.api -Require-Capability: \ - compile-only - -includeresource: {readme.md} -buildpath: \ diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index d72428857c8..43e82f80562 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -2,7 +2,7 @@ import org.osgi.annotation.versioning.ProviderType; -import io.openems.common.OpenemsException; +import io.openems.common.exceptions.OpenemsException; @ProviderType public interface MetadataService { diff --git a/io.openems.backend.metadata.odoo/bnd.bnd b/io.openems.backend.metadata.odoo/bnd.bnd index e5cca0e5409..11d07d8ae2d 100644 --- a/io.openems.backend.metadata.odoo/bnd.bnd +++ b/io.openems.backend.metadata.odoo/bnd.bnd @@ -15,7 +15,9 @@ Private-Package: \ io.openems.backend.metadata.api;version=latest,\ io.openems.common;version=latest,\ org.apache.servicemix.bundles.xmlrpc-client,\ - com.google.gson + com.google.gson,\ + com.google.guava,\ + io.openems.backend.common;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java index 300644e7c8f..bdffc3fbe46 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java @@ -6,8 +6,10 @@ import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.backend.common.types.DeviceImpl; import io.openems.backend.metadata.api.MetadataService; -import io.openems.common.OpenemsException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; import java.io.BufferedReader; import java.io.IOException; @@ -18,10 +20,13 @@ import java.net.URL; import java.net.URLEncoder; +import com.google.common.collect.LinkedHashMultimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -@Designate(ocd = OdooImpl.Config.class, factory = true) +@Designate(ocd = OdooImpl.Config.class, factory = false) @Component(name = "io.openems.backend.metadata.odoo") public class OdooImpl implements MetadataService { @@ -37,7 +42,7 @@ public class OdooImpl implements MetadataService { private String database; private int uid; private String password; - + @Activate void activate(Config config) { this.url = config.url(); @@ -79,7 +84,7 @@ public void getInfoWithSession() throws OpenemsException { // throw new OpenemsException("Session-ID is missing."); // } // String sessionId = data.getOdooSessionId().get(); - String sessionId = ""; + String sessionId = "8635d53109cafc9d51de443c7d2bc4e980ba1b5d"; // send request to Odoo String charset = "US-ASCII"; @@ -101,33 +106,33 @@ public void getInfoWithSession() throws OpenemsException { BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line = null; while ((line = br.readLine()) != null) { - System.out.println(line); JsonObject j = (new JsonParser()).parse(line).getAsJsonObject(); -// if (j.has("error")) { -// JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); -// String errorMessage = JsonUtils.getAsString(jError, "message"); -// throw new OpenemsException("Odoo replied with error: " + errorMessage); -// } + if (j.has("error")) { + JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); + String errorMessage = JsonUtils.getAsString(jError, "message"); + throw new OpenemsException("Odoo replied with error: " + errorMessage); + } -// if (j.has("result")) { -// // parse the result -// JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); -// JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); + if (j.has("result")) { + // parse the result + JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); + JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); // data.setUserId(JsonUtils.getAsInt(jUser, "id")); // data.setUserName(JsonUtils.getAsString(jUser, "name")); -// JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); -// LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); -// for (JsonElement jDevice : jDevices) { -// String name = JsonUtils.getAsString(jDevice, "name"); -// deviceMap.put(name, new DeviceImpl( // -// name, // -// JsonUtils.getAsString(jDevice, "comment"), // -// JsonUtils.getAsString(jDevice, "producttype"), // -// JsonUtils.getAsString(jDevice, "role"))); -// } + JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); + LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); + for (JsonElement jDevice : jDevices) { + String name = JsonUtils.getAsString(jDevice, "name"); + deviceMap.put(name, new DeviceImpl( // + name, // + JsonUtils.getAsString(jDevice, "comment"), // + JsonUtils.getAsString(jDevice, "producttype"), // + JsonUtils.getAsString(jDevice, "role"))); + } + System.out.println(deviceMap); // data.setDevices(deviceMap); -// return; -// } + return; + } } } catch (IOException e) { throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); diff --git a/io.openems.common/.settings/org.eclipse.core.resources.prefs b/io.openems.common/.settings/org.eclipse.core.resources.prefs index 1ba98e2695e..b7701f5d0cb 100644 --- a/io.openems.common/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.common/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,4 @@ eclipse.preferences.version=1 -encoding//src/io/openems/common/ExampleService.java=UTF-8 -encoding//src/io/openems/common/package-info.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 71a501522e5..1630618fd03 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -8,13 +8,16 @@ Bundle-Description: \ \ ${warning;Please update this Bundle-Description in bnd.bnd} -Export-Package: \ - io.openems.common +Export-Package: \ + io.openems.common.session,\ + io.openems.common.utils,\ + io.openems.common.exceptions -includeresource: {readme.md} --buildpath: \ - osgi.enroute.base.api;version=2.1 +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + com.google.gson -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.common/src/io/openems/common/ExampleService.java b/io.openems.common/src/io/openems/common/ExampleService.java deleted file mode 100644 index e8a7672506b..00000000000 --- a/io.openems.common/src/io/openems/common/ExampleService.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.openems.common; - -import org.osgi.annotation.versioning.ProviderType; - -/** - * This is an example OSGi enRoute bundle that has a component that implements an - * API. - */ - -@ProviderType -public interface ExampleService { - - /** - * The interface is a minimal method. - * - * @param message the message to say - * @return true if the message could be spoken - */ - boolean say(String message); - -} diff --git a/common/src/io/openems/common/exceptions/NotImplementedException.java b/io.openems.common/src/io/openems/common/exceptions/NotImplementedException.java similarity index 100% rename from common/src/io/openems/common/exceptions/NotImplementedException.java rename to io.openems.common/src/io/openems/common/exceptions/NotImplementedException.java diff --git a/io.openems.common/src/io/openems/common/OpenemsException.java b/io.openems.common/src/io/openems/common/exceptions/OpenemsException.java similarity index 87% rename from io.openems.common/src/io/openems/common/OpenemsException.java rename to io.openems.common/src/io/openems/common/exceptions/OpenemsException.java index 4b361f18120..483859f862b 100644 --- a/io.openems.common/src/io/openems/common/OpenemsException.java +++ b/io.openems.common/src/io/openems/common/exceptions/OpenemsException.java @@ -1,4 +1,4 @@ -package io.openems.common; +package io.openems.common.exceptions; public class OpenemsException extends Exception { diff --git a/io.openems.common/src/io/openems/common/exceptions/package-info.java b/io.openems.common/src/io/openems/common/exceptions/package-info.java new file mode 100644 index 00000000000..332031b345b --- /dev/null +++ b/io.openems.common/src/io/openems/common/exceptions/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common.exceptions; diff --git a/common/src/io/openems/common/session/Role.java b/io.openems.common/src/io/openems/common/session/Role.java similarity index 94% rename from common/src/io/openems/common/session/Role.java rename to io.openems.common/src/io/openems/common/session/Role.java index fc2a2f05114..3d6c44e55ca 100644 --- a/common/src/io/openems/common/session/Role.java +++ b/io.openems.common/src/io/openems/common/session/Role.java @@ -1,23 +1,23 @@ -package io.openems.common.session; - -public enum Role { - ADMIN, INSTALLER, OWNER, GUEST; - - public static Role getRole(String name) { - switch (name.toLowerCase()) { - case "admin": - return ADMIN; - case "installer": - return INSTALLER; - case "owner": - return OWNER; - case "guest": - default: - return GUEST; - } - } - - public static Role getDefaultRole() { - return GUEST; - } -} +package io.openems.common.session; + +public enum Role { + ADMIN, INSTALLER, OWNER, GUEST; + + public static Role getRole(String name) { + switch (name.toLowerCase()) { + case "admin": + return ADMIN; + case "installer": + return INSTALLER; + case "owner": + return OWNER; + case "guest": + default: + return GUEST; + } + } + + public static Role getDefaultRole() { + return GUEST; + } +} diff --git a/io.openems.common/src/io/openems/common/session/package-info.java b/io.openems.common/src/io/openems/common/session/package-info.java new file mode 100644 index 00000000000..810798b7f31 --- /dev/null +++ b/io.openems.common/src/io/openems/common/session/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common.session; diff --git a/common/src/io/openems/common/utils/JsonUtils.java b/io.openems.common/src/io/openems/common/utils/JsonUtils.java similarity index 96% rename from common/src/io/openems/common/utils/JsonUtils.java rename to io.openems.common/src/io/openems/common/utils/JsonUtils.java index 124da550902..1e58382ee91 100644 --- a/common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -1,337 +1,337 @@ -package io.openems.common.utils; - -import java.net.Inet4Address; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; - -import io.openems.common.exceptions.NotImplementedException; -import io.openems.common.exceptions.OpenemsException; - -// TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions -public class JsonUtils { - public static JsonArray getAsJsonArray(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonArray()) { - throw new OpenemsException("This is not a JsonArray: " + jElement); - } - return jElement.getAsJsonArray(); - }; - - public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jSubElement = getSubElement(jElement, memberName); - if (!jSubElement.isJsonArray()) { - throw new OpenemsException("Element [" + memberName + "] is not a JsonArray: " + jSubElement); - } - return jSubElement.getAsJsonArray(); - }; - - public static Optional getAsOptionalJsonArray(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsJsonArray(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonObject()) { - throw new OpenemsException("This is not a JsonObject: " + jElement); - } - return jElement.getAsJsonObject(); - }; - - public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jsubElement = getSubElement(jElement, memberName); - if (!jsubElement.isJsonObject()) { - throw new OpenemsException("Element [" + memberName + "] is not a JsonObject: " + jsubElement); - } - return jsubElement.getAsJsonObject(); - }; - - public static Optional getAsOptionalJsonObject(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsJsonObject(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jSubElement = getSubElement(jElement, memberName); - return getAsPrimitive(jSubElement); - } - - public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonPrimitive()) { - throw new OpenemsException("This is not a JsonPrimitive: " + jElement); - } - return jElement.getAsJsonPrimitive(); - } - - public static String getAsString(JsonElement jElement) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isString()) { - throw new OpenemsException("This is not a String: " + jPrimitive); - } - return jPrimitive.getAsString(); - } - - public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isBoolean()) { - throw new OpenemsException("This is not a Boolean: " + jPrimitive); - } - return jPrimitive.getAsBoolean(); - } - - public static Optional getAsOptionalString(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsString(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static String getAsString(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isString()) { - throw new OpenemsException("Element [" + memberName + "] is not a String: " + jPrimitive); - } - return jPrimitive.getAsString(); - } - - public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsInt(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static Optional getAsOptionalLong(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsLong(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsInt(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Integer.parseInt(string); - } - throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); - } - - public static boolean getAsBoolean(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isBoolean()) { - throw new OpenemsException("Element [" + memberName + "] is not a Boolean: " + jPrimitive); - } - return jPrimitive.getAsBoolean(); - } - - /** - * Takes a json in the form 'YYYY-MM-DD' and converts it to a ZonedDateTime with - * hour, minute and second set to zero. - * - * @param jElement - * @param memberName - * @param timezone - * @return - * @throws OpenemsException - */ - public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone) - throws OpenemsException { - String[] date = JsonUtils.getAsString(jElement, memberName).split("-"); - try { - int year = Integer.valueOf(date[0]); - int month = Integer.valueOf(date[1]); - int day = Integer.valueOf(date[2]); - return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone); - } catch (ArrayIndexOutOfBoundsException e) { - throw new OpenemsException("Element [" + memberName + "] is not a Date: " + jElement + ". Error: " + e); - } - } - - public static long getAsLong(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsLong(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Long.parseLong(string); - } - throw new OpenemsException("[" + memberName + "] is not a Number: " + jPrimitive); - } - - public static JsonElement getSubElement(JsonElement jElement, String memberName) throws OpenemsException { - JsonObject jObject = getAsJsonObject(jElement); - if (!jObject.has(memberName)) { - throw new OpenemsException("Element [" + memberName + "] is not a Subelement of: " + jElement); - } - return jObject.get(memberName); - } - - /** - * Merges the second Object into the first object - * - * @param j1 - * @param j2 - * @return - */ - public static JsonObject merge(JsonObject j1, JsonObject j2) { - // TODO be smarter: merge down the tree - for (Entry entry : j2.entrySet()) { - j1.add(entry.getKey(), entry.getValue()); - } - return j1; - } - - public static Optional merge(Optional j1Opt, Optional j2Opt) { - if (j1Opt.isPresent() && j2Opt.isPresent()) { - return Optional.of(JsonUtils.merge(j1Opt.get(), j2Opt.get())); - } - if (j1Opt.isPresent()) { - return j1Opt; - } - return j2Opt; - } - - public static boolean hasElement(JsonElement j, String... paths) { - return getMatchingElements(j, paths).size() > 0; - } - - public static Set getMatchingElements(JsonElement j, String... paths) { - Set result = new HashSet(); - if (paths.length == 0) { - // last path element - result.add(j); - return result; - } - String path = paths[0]; - if (j.isJsonObject()) { - JsonObject jO = j.getAsJsonObject(); - if (jO.has(path)) { - List nextPathsList = new ArrayList(Arrays.asList(paths)); - nextPathsList.remove(0); - String[] nextPaths = nextPathsList.toArray(new String[0]); - result.addAll(getMatchingElements(jO.get(path), nextPaths)); - } - } else if (j.isJsonArray()) { - for (JsonElement jE : j.getAsJsonArray()) { - result.addAll(getMatchingElements(jE, paths)); - } - } else if (j.isJsonPrimitive()) { - JsonPrimitive jP = j.getAsJsonPrimitive(); - if (jP.isString()) { - if (jP.getAsString().equals(path)) { - result.add(jP); - } - } - } - return result; - } - - /** - * Pretty print a JsonElement - * - * @param j - */ - public static void prettyPrint(JsonElement j) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(j); - System.out.println(json); - } - - /** - * Parses a string to a JsonElement - * - * @param string - * @return - */ - public static JsonElement parse(String string) throws OpenemsException { - try { - JsonParser parser = new JsonParser(); - return parser.parse(string); - } catch (JsonParseException e) { - throw new OpenemsException("Unable to parse [" + string + "] + to JSON: " + e.getMessage(), e); - } - } - - /* - * Copied from edge - * TODO! - */ - public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { - // null - if (value == null) { - return null; - } - // optional - if (value instanceof Optional) { - if (!((Optional) value).isPresent()) { - return null; - } else { - value = ((Optional) value).get(); - } - } - if (value instanceof Number) { - /* - * Number - */ - return new JsonPrimitive((Number) value); - } else if (value instanceof String) { - /* - * String - */ - return new JsonPrimitive((String) value); - } else if (value instanceof Boolean) { - /* - * Boolean - */ - return new JsonPrimitive((Boolean) value); - } else if (value instanceof Inet4Address) { - /* - * Inet4Address - */ - return new JsonPrimitive(((Inet4Address) value).getHostAddress()); - } else if (value instanceof JsonElement) { - /* - * JsonElement - */ - return (JsonElement) value; - } else if (value instanceof Long[]){ - /* - * Long-Array - */ - JsonArray js = new JsonArray(); - for (Long l : (Long[]) value){ - js.add(new JsonPrimitive((Long) l)); - } - return js; - } - throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // - + value.getClass().getSimpleName() + "]" // - + " to JSON is not implemented."); - } -} +package io.openems.common.utils; + +import java.net.Inet4Address; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +import io.openems.common.exceptions.NotImplementedException; +import io.openems.common.exceptions.OpenemsException; + +// TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions +public class JsonUtils { + public static JsonArray getAsJsonArray(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonArray()) { + throw new OpenemsException("This is not a JsonArray: " + jElement); + } + return jElement.getAsJsonArray(); + }; + + public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jSubElement = getSubElement(jElement, memberName); + if (!jSubElement.isJsonArray()) { + throw new OpenemsException("Element [" + memberName + "] is not a JsonArray: " + jSubElement); + } + return jSubElement.getAsJsonArray(); + }; + + public static Optional getAsOptionalJsonArray(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsJsonArray(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonObject()) { + throw new OpenemsException("This is not a JsonObject: " + jElement); + } + return jElement.getAsJsonObject(); + }; + + public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jsubElement = getSubElement(jElement, memberName); + if (!jsubElement.isJsonObject()) { + throw new OpenemsException("Element [" + memberName + "] is not a JsonObject: " + jsubElement); + } + return jsubElement.getAsJsonObject(); + }; + + public static Optional getAsOptionalJsonObject(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsJsonObject(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jSubElement = getSubElement(jElement, memberName); + return getAsPrimitive(jSubElement); + } + + public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonPrimitive()) { + throw new OpenemsException("This is not a JsonPrimitive: " + jElement); + } + return jElement.getAsJsonPrimitive(); + } + + public static String getAsString(JsonElement jElement) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isString()) { + throw new OpenemsException("This is not a String: " + jPrimitive); + } + return jPrimitive.getAsString(); + } + + public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isBoolean()) { + throw new OpenemsException("This is not a Boolean: " + jPrimitive); + } + return jPrimitive.getAsBoolean(); + } + + public static Optional getAsOptionalString(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsString(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static String getAsString(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (!jPrimitive.isString()) { + throw new OpenemsException("Element [" + memberName + "] is not a String: " + jPrimitive); + } + return jPrimitive.getAsString(); + } + + public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsInt(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static Optional getAsOptionalLong(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsLong(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsInt(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Integer.parseInt(string); + } + throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); + } + + public static boolean getAsBoolean(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (!jPrimitive.isBoolean()) { + throw new OpenemsException("Element [" + memberName + "] is not a Boolean: " + jPrimitive); + } + return jPrimitive.getAsBoolean(); + } + + /** + * Takes a json in the form 'YYYY-MM-DD' and converts it to a ZonedDateTime with + * hour, minute and second set to zero. + * + * @param jElement + * @param memberName + * @param timezone + * @return + * @throws OpenemsException + */ + public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone) + throws OpenemsException { + String[] date = JsonUtils.getAsString(jElement, memberName).split("-"); + try { + int year = Integer.valueOf(date[0]); + int month = Integer.valueOf(date[1]); + int day = Integer.valueOf(date[2]); + return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone); + } catch (ArrayIndexOutOfBoundsException e) { + throw new OpenemsException("Element [" + memberName + "] is not a Date: " + jElement + ". Error: " + e); + } + } + + public static long getAsLong(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsLong(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Long.parseLong(string); + } + throw new OpenemsException("[" + memberName + "] is not a Number: " + jPrimitive); + } + + public static JsonElement getSubElement(JsonElement jElement, String memberName) throws OpenemsException { + JsonObject jObject = getAsJsonObject(jElement); + if (!jObject.has(memberName)) { + throw new OpenemsException("Element [" + memberName + "] is not a Subelement of: " + jElement); + } + return jObject.get(memberName); + } + + /** + * Merges the second Object into the first object + * + * @param j1 + * @param j2 + * @return + */ + public static JsonObject merge(JsonObject j1, JsonObject j2) { + // TODO be smarter: merge down the tree + for (Entry entry : j2.entrySet()) { + j1.add(entry.getKey(), entry.getValue()); + } + return j1; + } + + public static Optional merge(Optional j1Opt, Optional j2Opt) { + if (j1Opt.isPresent() && j2Opt.isPresent()) { + return Optional.of(JsonUtils.merge(j1Opt.get(), j2Opt.get())); + } + if (j1Opt.isPresent()) { + return j1Opt; + } + return j2Opt; + } + + public static boolean hasElement(JsonElement j, String... paths) { + return getMatchingElements(j, paths).size() > 0; + } + + public static Set getMatchingElements(JsonElement j, String... paths) { + Set result = new HashSet(); + if (paths.length == 0) { + // last path element + result.add(j); + return result; + } + String path = paths[0]; + if (j.isJsonObject()) { + JsonObject jO = j.getAsJsonObject(); + if (jO.has(path)) { + List nextPathsList = new ArrayList(Arrays.asList(paths)); + nextPathsList.remove(0); + String[] nextPaths = nextPathsList.toArray(new String[0]); + result.addAll(getMatchingElements(jO.get(path), nextPaths)); + } + } else if (j.isJsonArray()) { + for (JsonElement jE : j.getAsJsonArray()) { + result.addAll(getMatchingElements(jE, paths)); + } + } else if (j.isJsonPrimitive()) { + JsonPrimitive jP = j.getAsJsonPrimitive(); + if (jP.isString()) { + if (jP.getAsString().equals(path)) { + result.add(jP); + } + } + } + return result; + } + + /** + * Pretty print a JsonElement + * + * @param j + */ + public static void prettyPrint(JsonElement j) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(j); + System.out.println(json); + } + + /** + * Parses a string to a JsonElement + * + * @param string + * @return + */ + public static JsonElement parse(String string) throws OpenemsException { + try { + JsonParser parser = new JsonParser(); + return parser.parse(string); + } catch (JsonParseException e) { + throw new OpenemsException("Unable to parse [" + string + "] + to JSON: " + e.getMessage(), e); + } + } + + /* + * Copied from edge + * TODO! + */ + public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { + // null + if (value == null) { + return null; + } + // optional + if (value instanceof Optional) { + if (!((Optional) value).isPresent()) { + return null; + } else { + value = ((Optional) value).get(); + } + } + if (value instanceof Number) { + /* + * Number + */ + return new JsonPrimitive((Number) value); + } else if (value instanceof String) { + /* + * String + */ + return new JsonPrimitive((String) value); + } else if (value instanceof Boolean) { + /* + * Boolean + */ + return new JsonPrimitive((Boolean) value); + } else if (value instanceof Inet4Address) { + /* + * Inet4Address + */ + return new JsonPrimitive(((Inet4Address) value).getHostAddress()); + } else if (value instanceof JsonElement) { + /* + * JsonElement + */ + return (JsonElement) value; + } else if (value instanceof Long[]){ + /* + * Long-Array + */ + JsonArray js = new JsonArray(); + for (Long l : (Long[]) value){ + js.add(new JsonPrimitive((Long) l)); + } + return js; + } + throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // + + value.getClass().getSimpleName() + "]" // + + " to JSON is not implemented."); + } +} diff --git a/io.openems.common/src/io/openems/common/package-info.java b/io.openems.common/src/io/openems/common/utils/package-info.java similarity index 59% rename from io.openems.common/src/io/openems/common/package-info.java rename to io.openems.common/src/io/openems/common/utils/package-info.java index 23c6837cfd8..3531729739d 100644 --- a/io.openems.common/src/io/openems/common/package-info.java +++ b/io.openems.common/src/io/openems/common/utils/package-info.java @@ -1,2 +1,2 @@ @org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.common; +package io.openems.common.utils; From 0a906c30a4fed997fe68668f8061bb6ce4fc5e67 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 26 Jan 2018 09:21:39 +0100 Subject: [PATCH 014/156] Add Timedata api --- io.openems.backend.metadata.api/bnd.bnd | 4 +- .../backend/metadata/api}/MetadataDevice.java | 4 +- .../metadata/api}/MetadataDevices.java | 2 +- io.openems.backend.timedata/.classpath | 8 ++ io.openems.backend.timedata/.gitignore | 3 + io.openems.backend.timedata/.project | 23 +++++ .../org.eclipse.core.resources.prefs | 6 ++ .../.settings/org.eclipse.jdt.core.prefs | 11 +++ io.openems.backend.timedata/bnd.bnd | 28 ++++++ io.openems.backend.timedata/readme.md | 8 ++ .../backend/timedata/TimedataService.java | 10 ++- .../backend/timedata/package-info.java | 2 + io.openems.backend.timedata/test/.gitignore | 0 io.openems.common/bnd.bnd | 4 +- .../io/openems/common/api/TimedataSource.java | 0 .../io/openems/common/api/package-info.java | 2 + .../openems/common/types/ChannelAddress.java | 86 +++++++++---------- .../io/openems/common/types/package-info.java | 2 + 18 files changed, 151 insertions(+), 52 deletions(-) rename {backend/src/main/java/io/openems/backend/metadata/api/device => io.openems.backend.metadata.api/src/io/openems/backend/metadata/api}/MetadataDevice.java (92%) rename {backend/src/main/java/io/openems/backend/metadata/api/device => io.openems.backend.metadata.api/src/io/openems/backend/metadata/api}/MetadataDevices.java (95%) create mode 100644 io.openems.backend.timedata/.classpath create mode 100644 io.openems.backend.timedata/.gitignore create mode 100644 io.openems.backend.timedata/.project create mode 100644 io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.timedata/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.timedata/bnd.bnd create mode 100644 io.openems.backend.timedata/readme.md rename backend/src/main/java/io/openems/backend/timedata/api/TimedataSingleton.java => io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java (71%) create mode 100644 io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java create mode 100644 io.openems.backend.timedata/test/.gitignore rename {common => io.openems.common}/src/io/openems/common/api/TimedataSource.java (100%) create mode 100644 io.openems.common/src/io/openems/common/api/package-info.java rename {common => io.openems.common}/src/io/openems/common/types/ChannelAddress.java (96%) create mode 100644 io.openems.common/src/io/openems/common/types/package-info.java diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd index 20cf5875684..f14d9e2c2db 100644 --- a/io.openems.backend.metadata.api/bnd.bnd +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -12,7 +12,9 @@ Export-Package: \ -buildpath: \ osgi.enroute.base.api;version=2.1,\ - io.openems.common;version=latest + io.openems.common;version=latest,\ + io.openems.backend.common;version=latest,\ + com.google.gson -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDevice.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java similarity index 92% rename from backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDevice.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java index 2b164592d9c..2214f52bd3e 100644 --- a/backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDevice.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java @@ -1,11 +1,11 @@ -package io.openems.backend.metadata.api.device; +package io.openems.backend.metadata.api; import java.util.Optional; import com.google.gson.JsonObject; +import io.openems.backend.common.types.Device; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.Device; public interface MetadataDevice extends Device { diff --git a/backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDevices.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevices.java similarity index 95% rename from backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDevices.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevices.java index da8c31be7cc..d6f2fa5e371 100644 --- a/backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDevices.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevices.java @@ -1,4 +1,4 @@ -package io.openems.backend.metadata.api.device; +package io.openems.backend.metadata.api; import java.util.ArrayList; import java.util.HashSet; diff --git a/io.openems.backend.timedata/.classpath b/io.openems.backend.timedata/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.timedata/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.timedata/.gitignore b/io.openems.backend.timedata/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.timedata/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.timedata/.project b/io.openems.backend.timedata/.project new file mode 100644 index 00000000000..26c9852c83b --- /dev/null +++ b/io.openems.backend.timedata/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.timedata + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..5e6bced0308 --- /dev/null +++ b/io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/timedata/TimedataService.java=UTF-8 +encoding//src/io/openems/backend/timedata/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.timedata/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.timedata/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.timedata/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.timedata/bnd.bnd b/io.openems.backend.timedata/bnd.bnd new file mode 100644 index 00000000000..c3bc55f0e85 --- /dev/null +++ b/io.openems.backend.timedata/bnd.bnd @@ -0,0 +1,28 @@ +# +# io.openems.backend.timedata DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} +Bundle-Description: \ + This project contains a complete example with API, provider, and JUnit test code. \ + \ + ${warning;Please update this Bundle-Description in bnd.bnd} + +Export-Package: \ + io.openems.backend.timedata + +Require-Capability: \ + compile-only + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.metadata.api;version=latest,\ + com.google.gson,\ + io.openems.common;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.timedata/readme.md b/io.openems.backend.timedata/readme.md new file mode 100644 index 00000000000..b785de0cb58 --- /dev/null +++ b/io.openems.backend.timedata/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.timedata + +${Bundle-Description} + +## Example + +## References + diff --git a/backend/src/main/java/io/openems/backend/timedata/api/TimedataSingleton.java b/io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java similarity index 71% rename from backend/src/main/java/io/openems/backend/timedata/api/TimedataSingleton.java rename to io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java index 3ac8517671b..e106dc2ed86 100644 --- a/backend/src/main/java/io/openems/backend/timedata/api/TimedataSingleton.java +++ b/io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java @@ -1,14 +1,16 @@ -package io.openems.backend.timedata.api; +package io.openems.backend.timedata; import java.util.Optional; +import org.osgi.annotation.versioning.ProviderType; + import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.device.MetadataDevices; -import io.openems.common.api.TimedataSource; +import io.openems.backend.metadata.api.MetadataDevices; import io.openems.common.types.ChannelAddress; -public interface TimedataSingleton extends TimedataSource { +@ProviderType +public interface TimedataService { /** * Takes a JsonObject and writes the points to database. * diff --git a/io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java b/io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java new file mode 100644 index 00000000000..9238f22587f --- /dev/null +++ b/io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.timedata; diff --git a/io.openems.backend.timedata/test/.gitignore b/io.openems.backend.timedata/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd index 1630618fd03..5dc439927f8 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -11,7 +11,9 @@ Bundle-Description: \ Export-Package: \ io.openems.common.session,\ io.openems.common.utils,\ - io.openems.common.exceptions + io.openems.common.exceptions,\ + io.openems.common.types,\ + io.openems.common.api -includeresource: {readme.md} diff --git a/common/src/io/openems/common/api/TimedataSource.java b/io.openems.common/src/io/openems/common/api/TimedataSource.java similarity index 100% rename from common/src/io/openems/common/api/TimedataSource.java rename to io.openems.common/src/io/openems/common/api/TimedataSource.java diff --git a/io.openems.common/src/io/openems/common/api/package-info.java b/io.openems.common/src/io/openems/common/api/package-info.java new file mode 100644 index 00000000000..39207229641 --- /dev/null +++ b/io.openems.common/src/io/openems/common/api/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common.api; diff --git a/common/src/io/openems/common/types/ChannelAddress.java b/io.openems.common/src/io/openems/common/types/ChannelAddress.java similarity index 96% rename from common/src/io/openems/common/types/ChannelAddress.java rename to io.openems.common/src/io/openems/common/types/ChannelAddress.java index 591090fa783..a5f805e9ef0 100644 --- a/common/src/io/openems/common/types/ChannelAddress.java +++ b/io.openems.common/src/io/openems/common/types/ChannelAddress.java @@ -1,43 +1,43 @@ -package io.openems.common.types; - -import io.openems.common.exceptions.OpenemsException; - -public class ChannelAddress implements Comparable { - private final String thingId; - private final String channelId; - - public ChannelAddress(String thingId, String channelId) { - super(); - this.thingId = thingId; - this.channelId = channelId; - } - - public String getThingId() { - return thingId; - } - - public String getChannelId() { - return channelId; - } - - @Override - public String toString() { - return thingId + "/" + channelId; - } - - public static ChannelAddress fromString(String address) throws OpenemsException { - try { - String[] addressArray = address.split("/"); - String thingId = addressArray[0]; - String channelId = addressArray[1]; - return new ChannelAddress(thingId, channelId); - } catch (Exception e) { - throw new OpenemsException("This [" + address + "] is not a valid channel address."); - } - } - - @Override - public int compareTo(ChannelAddress other) { - return this.toString().compareTo(other.toString()); - } -} +package io.openems.common.types; + +import io.openems.common.exceptions.OpenemsException; + +public class ChannelAddress implements Comparable { + private final String thingId; + private final String channelId; + + public ChannelAddress(String thingId, String channelId) { + super(); + this.thingId = thingId; + this.channelId = channelId; + } + + public String getThingId() { + return thingId; + } + + public String getChannelId() { + return channelId; + } + + @Override + public String toString() { + return thingId + "/" + channelId; + } + + public static ChannelAddress fromString(String address) throws OpenemsException { + try { + String[] addressArray = address.split("/"); + String thingId = addressArray[0]; + String channelId = addressArray[1]; + return new ChannelAddress(thingId, channelId); + } catch (Exception e) { + throw new OpenemsException("This [" + address + "] is not a valid channel address."); + } + } + + @Override + public int compareTo(ChannelAddress other) { + return this.toString().compareTo(other.toString()); + } +} diff --git a/io.openems.common/src/io/openems/common/types/package-info.java b/io.openems.common/src/io/openems/common/types/package-info.java new file mode 100644 index 00000000000..144a9148394 --- /dev/null +++ b/io.openems.common/src/io/openems/common/types/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common.types; From d5fa5090c458653e072c98859a0a83f289a319ac Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 26 Jan 2018 11:56:11 +0100 Subject: [PATCH 015/156] Implement InfluxDB timedata provider --- .../io/openems/backend/timedata/Timedata.java | 51 ----- cnf/central.xml | 20 ++ io.openems.backend.application/bnd.bnd | 3 +- .../io.openems.backend.application.bndrun | 16 +- .../backend/application/BackendApp.java | 7 +- .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 10 +- .../readme.md | 0 .../timedata/api}/TimedataService.java | 2 +- .../backend/timedata/api}/package-info.java | 2 +- .../test/.gitignore | 0 io.openems.backend.timedata.influx/.classpath | 8 + io.openems.backend.timedata.influx/.gitignore | 3 + io.openems.backend.timedata.influx/.project | 23 ++ .../org.eclipse.core.resources.prefs | 7 + .../.settings/org.eclipse.jdt.core.prefs | 11 + io.openems.backend.timedata.influx/bnd.bnd | 27 +++ .../debug.bndrun | 13 ++ .../io.openems.backend.timedata.influx.bndrun | 14 ++ io.openems.backend.timedata.influx/readme.md | 8 + .../timedata/influx/api/InfluxImpl.java | 211 ++++++++++-------- .../influx/internal}/DeviceCache.java | 2 +- .../timedata/influx/ProviderImplTest.java | 26 +++ .../org.eclipse.core.resources.prefs | 6 - 28 files changed, 309 insertions(+), 169 deletions(-) delete mode 100644 backend/src/main/java/io/openems/backend/timedata/Timedata.java rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/.classpath (100%) rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/.gitignore (100%) rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/.project (91%) create mode 100644 io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/bnd.bnd (58%) rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/readme.md (100%) rename {io.openems.backend.timedata/src/io/openems/backend/timedata => io.openems.backend.timedata.api/src/io/openems/backend/timedata/api}/TimedataService.java (94%) rename {io.openems.backend.timedata/src/io/openems/backend/timedata => io.openems.backend.timedata.api/src/io/openems/backend/timedata/api}/package-info.java (54%) rename {io.openems.backend.timedata => io.openems.backend.timedata.api}/test/.gitignore (100%) create mode 100644 io.openems.backend.timedata.influx/.classpath create mode 100644 io.openems.backend.timedata.influx/.gitignore create mode 100644 io.openems.backend.timedata.influx/.project create mode 100644 io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.timedata.influx/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.timedata.influx/bnd.bnd create mode 100644 io.openems.backend.timedata.influx/debug.bndrun create mode 100644 io.openems.backend.timedata.influx/io.openems.backend.timedata.influx.bndrun create mode 100644 io.openems.backend.timedata.influx/readme.md rename backend/src/main/java/io/openems/backend/timedata/influx/InfluxdbSingleton.java => io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java (64%) rename {backend/src/main/java/io/openems/backend/timedata/influx => io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal}/DeviceCache.java (94%) create mode 100644 io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java delete mode 100644 io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs diff --git a/backend/src/main/java/io/openems/backend/timedata/Timedata.java b/backend/src/main/java/io/openems/backend/timedata/Timedata.java deleted file mode 100644 index d56dc810087..00000000000 --- a/backend/src/main/java/io/openems/backend/timedata/Timedata.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.openems.backend.timedata; - -import io.openems.backend.timedata.api.TimedataSingleton; -import io.openems.backend.timedata.dummy.TimedataDummySingleton; -import io.openems.backend.timedata.influx.InfluxdbSingleton; -import io.openems.common.exceptions.OpenemsException; - -/** - * Provider for Timedata singleton - * - * @author stefan.feilmeier - * - */ -public class Timedata { - - private static TimedataSingleton instance = null; - - /** - * Initialize InfluxDB object - * - * @param port - * @throws Exception - */ - public static void initializeInfluxdb(String database, String url, int port, String username, String password) - throws OpenemsException { - if (database == null || url == null || username == null || password == null) { - throw new OpenemsException("Config missing: database [" + database + "], url [" + url + "], port [" + port - + "] username [" + username + "], password [" + password + "]"); - } - Timedata.instance = new InfluxdbSingleton(database, url, port, username, password); - } - - /** - * Initialize Dummy provider - * - * @param port - * @throws Exception - */ - public static synchronized void initializeDummy() { - Timedata.instance = new TimedataDummySingleton(); - } - - /** - * Returns the singleton instance - * - * @return - */ - public static synchronized TimedataSingleton instance() { - return Timedata.instance; - } -} \ No newline at end of file diff --git a/cnf/central.xml b/cnf/central.xml index 060f2446da0..fb92f0ac46a 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -31,6 +31,26 @@ org.apache.servicemix.bundles.commons-httpclient 3.1_7 + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.influxdb-java + 2.8_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okhttp + 3.2.0_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okio + 1.13.0_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.retrofit + 2.3.0_1 + org.apache.servicemix.bundles org.apache.servicemix.bundles.xmlrpc-client diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index 8ec0af66d0b..1350996f7d1 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -13,7 +13,8 @@ Private-Package: \ -buildpath: \ osgi.enroute.base.api;version=2.1,\ io.openems.backend.metadata.api;version=latest,\ - io.openems.common;version=latest + io.openems.common;version=latest,\ + io.openems.backend.timedata.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 026f83972d1..3951c18cf55 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -18,15 +18,23 @@ JPM-Command: provider osgi.identity;filter:='(osgi.identity=org.apache.felix.webconsole)',\ osgi.identity;filter:='(osgi.identity=osgi.enroute.webconsole.xray.provider)',\ osgi.identity;filter:='(osgi.identity=com.google.guava)',\ - osgi.identity;filter:='(osgi.identity=io.openems.backend.common)' + osgi.identity;filter:='(osgi.identity=io.openems.backend.common)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx)',\ + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.influxdb-java)',\ + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.okhttp)',\ + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.okio)',\ + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.retrofit)' + -resolve: auto -runbundles: \ com.google.gson;version='[2.8.2,2.8.3)',\ - com.google.guava;version='[23.6.0,23.6.1)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ io.openems.backend.application;version=snapshot,\ io.openems.backend.common;version=snapshot,\ io.openems.backend.metadata.odoo;version=snapshot,\ + io.openems.backend.timedata.api;version=snapshot,\ + io.openems.backend.timedata.influx;version=snapshot,\ io.openems.common;version=snapshot,\ json;version='[20160212.0.0,20160212.0.1)',\ org.apache.commons.codec;version='[1.10.0,1.10.1)',\ @@ -39,6 +47,10 @@ JPM-Command: provider org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.apache.felix.webconsole;version='[4.2.16,4.2.17)',\ org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ + org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ + org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ + org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 02abb87cb3b..20888d385ea 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -6,6 +6,7 @@ import org.osgi.service.component.annotations.Reference; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.OpenemsException; @Component() @@ -13,11 +14,13 @@ public class BackendApp { @Reference MetadataService metadataService; + + @Reference + TimedataService timedataService; @Activate void activate() { - System.out.println("Activate"); - System.out.println("Meta: " + this.metadataService); + System.out.println("Activate BackendApp"); try { this.metadataService.getInfoWithSession(); } catch (OpenemsException e) { diff --git a/io.openems.backend.timedata/.classpath b/io.openems.backend.timedata.api/.classpath similarity index 100% rename from io.openems.backend.timedata/.classpath rename to io.openems.backend.timedata.api/.classpath diff --git a/io.openems.backend.timedata/.gitignore b/io.openems.backend.timedata.api/.gitignore similarity index 100% rename from io.openems.backend.timedata/.gitignore rename to io.openems.backend.timedata.api/.gitignore diff --git a/io.openems.backend.timedata/.project b/io.openems.backend.timedata.api/.project similarity index 91% rename from io.openems.backend.timedata/.project rename to io.openems.backend.timedata.api/.project index 26c9852c83b..426049a9c28 100644 --- a/io.openems.backend.timedata/.project +++ b/io.openems.backend.timedata.api/.project @@ -1,6 +1,6 @@ - io.openems.backend.timedata + io.openems.backend.timedata.api diff --git a/io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..2ba216d7d60 --- /dev/null +++ b/io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/timedata/api/TimedataService.java=UTF-8 +encoding//src/io/openems/backend/timedata/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.timedata/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.timedata.api/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.timedata/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.timedata.api/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.timedata/bnd.bnd b/io.openems.backend.timedata.api/bnd.bnd similarity index 58% rename from io.openems.backend.timedata/bnd.bnd rename to io.openems.backend.timedata.api/bnd.bnd index c3bc55f0e85..0090555f31f 100644 --- a/io.openems.backend.timedata/bnd.bnd +++ b/io.openems.backend.timedata.api/bnd.bnd @@ -3,16 +3,8 @@ # Bundle-Version: 1.0.0.${tstamp} -Bundle-Description: \ - This project contains a complete example with API, provider, and JUnit test code. \ - \ - ${warning;Please update this Bundle-Description in bnd.bnd} -Export-Package: \ - io.openems.backend.timedata - -Require-Capability: \ - compile-only +Export-Package: io.openems.backend.timedata.api -includeresource: {readme.md} diff --git a/io.openems.backend.timedata/readme.md b/io.openems.backend.timedata.api/readme.md similarity index 100% rename from io.openems.backend.timedata/readme.md rename to io.openems.backend.timedata.api/readme.md diff --git a/io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java similarity index 94% rename from io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java rename to io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index e106dc2ed86..686b199ddfe 100644 --- a/io.openems.backend.timedata/src/io/openems/backend/timedata/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -1,4 +1,4 @@ -package io.openems.backend.timedata; +package io.openems.backend.timedata.api; import java.util.Optional; diff --git a/io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/package-info.java similarity index 54% rename from io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java rename to io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/package-info.java index 9238f22587f..5888e0931bd 100644 --- a/io.openems.backend.timedata/src/io/openems/backend/timedata/package-info.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/package-info.java @@ -1,2 +1,2 @@ @org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.timedata; +package io.openems.backend.timedata.api; diff --git a/io.openems.backend.timedata/test/.gitignore b/io.openems.backend.timedata.api/test/.gitignore similarity index 100% rename from io.openems.backend.timedata/test/.gitignore rename to io.openems.backend.timedata.api/test/.gitignore diff --git a/io.openems.backend.timedata.influx/.classpath b/io.openems.backend.timedata.influx/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.timedata.influx/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.timedata.influx/.gitignore b/io.openems.backend.timedata.influx/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.timedata.influx/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.timedata.influx/.project b/io.openems.backend.timedata.influx/.project new file mode 100644 index 00000000000..209c2129b0a --- /dev/null +++ b/io.openems.backend.timedata.influx/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.timedata.influx + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..36bab44257a --- /dev/null +++ b/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/timedata/influx/api/InfluxImpl.java=UTF-8 +encoding//test/io/openems/backend/timedata/influx/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.timedata.influx.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.timedata.influx/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.timedata.influx/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.timedata.influx/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.timedata.influx/bnd.bnd b/io.openems.backend.timedata.influx/bnd.bnd new file mode 100644 index 00000000000..a5dea07bceb --- /dev/null +++ b/io.openems.backend.timedata.influx/bnd.bnd @@ -0,0 +1,27 @@ +# +# io.openems.backend.timedata.influx PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: \ + io.openems.backend.timedata.influx.api;-provide=true + +Private-Package: io.openems.backend.timedata.influx.internal + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.common;version=latest,\ + io.openems.backend.timedata.api;version=latest,\ + io.openems.backend.common;version=latest,\ + com.google.gson,\ + io.openems.backend.metadata.api;version=latest,\ + com.google.guava,\ + org.apache.servicemix.bundles.influxdb-java + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.timedata.influx/debug.bndrun b/io.openems.backend.timedata.influx/debug.bndrun new file mode 100644 index 00000000000..3d5f2b7c40a --- /dev/null +++ b/io.openems.backend.timedata.influx/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.timedata.influx DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.timedata.influx.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.timedata.influx/io.openems.backend.timedata.influx.bndrun b/io.openems.backend.timedata.influx/io.openems.backend.timedata.influx.bndrun new file mode 100644 index 00000000000..0c09a42456a --- /dev/null +++ b/io.openems.backend.timedata.influx/io.openems.backend.timedata.influx.bndrun @@ -0,0 +1,14 @@ +# +# io.openems.backend.timedata.influx LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.timedata.influx.launch +JPM-Command: provider + + +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx.provider)' + +-runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.timedata.influx/readme.md b/io.openems.backend.timedata.influx/readme.md new file mode 100644 index 00000000000..d5ca42cb7cf --- /dev/null +++ b/io.openems.backend.timedata.influx/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.timedata.influx Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/backend/src/main/java/io/openems/backend/timedata/influx/InfluxdbSingleton.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java similarity index 64% rename from backend/src/main/java/io/openems/backend/timedata/influx/InfluxdbSingleton.java rename to io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java index 12200ccad78..225b011dd30 100644 --- a/backend/src/main/java/io/openems/backend/timedata/influx/InfluxdbSingleton.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java @@ -1,8 +1,5 @@ -package io.openems.backend.timedata.influx; +package io.openems.backend.timedata.influx.api; -import java.text.NumberFormat; -import java.text.ParseException; -import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -15,77 +12,94 @@ import org.influxdb.dto.BatchPoints; import org.influxdb.dto.Point; import org.influxdb.dto.Point.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; import com.google.common.collect.TreeBasedTable; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import io.openems.backend.metadata.api.device.MetadataDevice; -import io.openems.backend.metadata.api.device.MetadataDevices; -import io.openems.backend.timedata.api.TimedataSingleton; +import io.openems.backend.metadata.api.MetadataDevice; +import io.openems.backend.metadata.api.MetadataDevices; +import io.openems.backend.timedata.api.TimedataService; +import io.openems.backend.timedata.influx.internal.DeviceCache; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; -import io.openems.common.utils.InfluxdbUtils; import io.openems.common.utils.JsonUtils; -public class InfluxdbSingleton implements TimedataSingleton { +import org.osgi.service.metatype.annotations.Designate; - private final Logger log = LoggerFactory.getLogger(InfluxdbSingleton.class); +@Designate(ocd = InfluxImpl.Config.class, factory = false) +@Component(name = "io.openems.backend.timedata.influx") +public class InfluxImpl implements TimedataService { - private final String MEASUREMENT = "data"; private final String TMP_MINI_MEASUREMENT = "minies"; + @ObjectClassDefinition + @interface Config { + String database(); + + String url(); + + int port(); + + String username(); + + String password(); + + String measurement() default "data"; + } + private String database; private String url; private int port; private String username; private String password; + private String measurement; + private InfluxDB influxDB; private final Map deviceCacheMap = new HashMap<>(); - public InfluxdbSingleton(String database, String url, int port, String username, String password) - throws OpenemsException { - this.database = database; - this.url = url; - this.port = port; - this.username = username; - this.password = password; - try { - this.connect(); - } catch (Exception e) { - throw new OpenemsException("Connecting to InfluxDB failed: " + e.getMessage()); - } + @Activate + void activate(Config config) throws OpenemsException { + System.out.println("Activate InfluxDB"); + this.database = config.database(); + this.url = config.url(); + this.port = config.port(); + this.username = config.username(); + this.password = config.password(); + this.measurement = config.measurement(); + // TODO: connect asynchronously to not block the activator +// try { +// this.connect(); +// } catch (Exception e) { +// throw new OpenemsException("Connecting to InfluxDB failed: " + e.getMessage()); +// } + } + + @Deactivate + void deactivate() { } private void connect() throws Exception { - InfluxDB influxDB = InfluxDBFactory.connect("http://" + url + ":" + port, username, password); - this.influxDB = influxDB; + this.influxDB = InfluxDBFactory.connect("http://" + url + ":" + port, username, password); try { influxDB.ping(); } catch (RuntimeException e) { - log.error("Unable to connect to InfluxDB: " + e.getMessage()); + System.out.println("Unable to connect to InfluxDB: " + e.getMessage()); + // TODO log.error("Unable to connect to InfluxDB: " + e.getMessage()); throw new Exception(e.getMessage()); } - /* - * try { - * influxDB.createDatabase(DB_NAME); - * } catch (RuntimeException e) { - * log.error("Unable to create InfluxDB database: " + DB_NAME); - * throw new Exception(e.getMessage()); - * } - */ } /** * Takes a JsonObject and writes the points to influxDB. * - * Format: { "timestamp1" { "channel1": value, "channel2": value }, - * "timestamp2" { "channel1": value, "channel2": value } } + * Format: { "timestamp1" { "channel1": value, "channel2": value }, "timestamp2" + * { "channel1": value, "channel2": value } } */ @Override public void write(MetadataDevices devices, JsonObject jData) { @@ -109,11 +123,13 @@ public void write(MetadataDevices devices, JsonObject jData) { jChannels = JsonUtils.getAsJsonObject(entry.getValue()); sortedData.put(timestamp, jChannels); } catch (OpenemsException e) { - log.error("Data error: " + e.getMessage()); + // TODO log.error("Data error: " + e.getMessage()); + System.out.println("Data error: " + e.getMessage()); } } - // Prepare data table. Takes entries starting with eldest timestamp (ascending order) + // Prepare data table. Takes entries starting with eldest timestamp (ascending + // order) for (Entry dataEntry : sortedData.entrySet()) { Long timestamp = dataEntry.getKey(); JsonObject jChannels = dataEntry.getValue(); @@ -123,7 +139,8 @@ public void write(MetadataDevices devices, JsonObject jData) { continue; } - // Check if cache is valid (it is not elder than 5 minutes compared to this timestamp) + // Check if cache is valid (it is not elder than 5 minutes compared to this + // timestamp) long cacheTimestamp = deviceCache.getTimestamp(); if (timestamp < cacheTimestamp) { // incoming data is older than cache -> do not apply cache @@ -144,8 +161,11 @@ public void write(MetadataDevices devices, JsonObject jData) { // cache is not anymore valid (elder than 5 minutes) // clear cache if (cacheTimestamp != 0l) { - log.info("Invalidate cache for device [" + deviceId + "]. This timestamp [" + timestamp - + "]. Cache timestamp [" + cacheTimestamp + "]"); + System.out.println("Invalidate cache for device [" + deviceId + "]. This timestamp [" + + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); + // TODO log.info("Invalidate cache for device [" + deviceId + "]. This timestamp [" + + // timestamp + // + "]. Cache timestamp [" + cacheTimestamp + "]"); } deviceCache.clear(); } @@ -188,6 +208,7 @@ public void write(MetadataDevices devices, JsonObject jData) { } } + // private void writeData(int deviceId, TreeBasedTable data) { BatchPoints batchPoints = BatchPoints.database(database) // .tag("fems", String.valueOf(deviceId)) // @@ -195,8 +216,8 @@ private void writeData(int deviceId, TreeBasedTable data) for (Entry> entry : data.rowMap().entrySet()) { Long timestamp = entry.getKey(); - Builder builder = Point.measurement(MEASUREMENT) // this builds an InfluxDB record ("point") for a given - // timestamp + Builder builder = Point.measurement(this.measurement) // this builds an InfluxDB record ("point") for a + // given timestamp .time(timestamp, TimeUnit.MILLISECONDS).fields(entry.getValue()); batchPoints.point(builder.build()); } @@ -206,8 +227,8 @@ private void writeData(int deviceId, TreeBasedTable data) } /** - * Writes data to old database for old Mini monitoring - * TODO remove after full migration + * Writes data to old database for old Mini monitoring TODO remove after full + * migration * * @param device * @param data @@ -279,53 +300,56 @@ private void writeDataToOldMiniMonitoring(MetadataDevice device, TreeBasedTable< * @return */ private Optional parseValue(String channel, Object value) { - if (value == null) { - return Optional.empty(); - } - // convert JsonElement - if (value instanceof JsonElement) { - JsonElement jValueElement = (JsonElement) value; - if (jValueElement.isJsonPrimitive()) { - JsonPrimitive jValue = jValueElement.getAsJsonPrimitive(); - if (jValue.isNumber()) { - try { - // Avoid GSONs LazilyParsedNumber - value = NumberFormat.getInstance().parse(jValue.toString()); - } catch (ParseException e) { - log.error("Unable to parse Number: " + e.getMessage()); - value = jValue.getAsNumber(); - } - } else if (jValue.isBoolean()) { - value = jValue.getAsBoolean(); - } else if (jValue.isString()) { - value = jValue.getAsString(); - } - } - } - if (value instanceof Number) { - Number numberValue = (Number) value; - if (numberValue instanceof Integer) { - return Optional.of(numberValue.intValue()); - } else if (numberValue instanceof Double) { - return Optional.of(numberValue.doubleValue()); - } else { - return Optional.of(numberValue); - } - } else if (value instanceof Boolean) { - return Optional.of((Boolean) value); - } else if (value instanceof String) { - return Optional.of((String) value); - } - log.warn("Unknown type of value [" + value + "] channel [" + channel + "]. This should never happen."); + // if (value == null) { + // return Optional.empty(); + // } + // // convert JsonElement + // if (value instanceof JsonElement) { + // JsonElement jValueElement = (JsonElement) value; + // if (jValueElement.isJsonPrimitive()) { + // JsonPrimitive jValue = jValueElement.getAsJsonPrimitive(); + // if (jValue.isNumber()) { + // try { + // // Avoid GSONs LazilyParsedNumber + // value = NumberFormat.getInstance().parse(jValue.toString()); + // } catch (ParseException e) { + // log.error("Unable to parse Number: " + e.getMessage()); + // value = jValue.getAsNumber(); + // } + // } else if (jValue.isBoolean()) { + // value = jValue.getAsBoolean(); + // } else if (jValue.isString()) { + // value = jValue.getAsString(); + // } + // } + // } + // if (value instanceof Number) { + // Number numberValue = (Number) value; + // if (numberValue instanceof Integer) { + // return Optional.of(numberValue.intValue()); + // } else if (numberValue instanceof Double) { + // return Optional.of(numberValue.doubleValue()); + // } else { + // return Optional.of(numberValue); + // } + // } else if (value instanceof Boolean) { + // return Optional.of((Boolean) value); + // } else if (value instanceof String) { + // return Optional.of((String) value); + // } + // log.warn("Unknown type of value [" + value + "] channel [" + channel + "]. + // This should never happen."); return Optional.empty(); } - @Override - public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, - JsonObject channels, int resolution) throws OpenemsException { - return InfluxdbUtils.queryHistoricData(influxDB, this.database, deviceIdOpt, fromDate, toDate, channels, - resolution); - } + // @Override + // public JsonArray queryHistoricData(Optional deviceIdOpt, + // ZonedDateTime fromDate, ZonedDateTime toDate, + // JsonObject channels, int resolution) throws OpenemsException { + // return InfluxdbUtils.queryHistoricData(influxDB, this.database, deviceIdOpt, + // fromDate, toDate, channels, + // resolution); + // } @Override public Optional getChannelValue(int deviceId, ChannelAddress channelAddress) { @@ -335,6 +359,5 @@ public Optional getChannelValue(int deviceId, ChannelAddress channelAddr } else { return Optional.empty(); } - } } diff --git a/backend/src/main/java/io/openems/backend/timedata/influx/DeviceCache.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/DeviceCache.java similarity index 94% rename from backend/src/main/java/io/openems/backend/timedata/influx/DeviceCache.java rename to io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/DeviceCache.java index 4c9c565d79b..60ca0d9218c 100644 --- a/backend/src/main/java/io/openems/backend/timedata/influx/DeviceCache.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/DeviceCache.java @@ -1,4 +1,4 @@ -package io.openems.backend.timedata.influx; +package io.openems.backend.timedata.influx.internal; import java.util.HashMap; import java.util.Map; diff --git a/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java b/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java new file mode 100644 index 00000000000..2b8a50387e7 --- /dev/null +++ b/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java @@ -0,0 +1,26 @@ +package io.openems.backend.timedata.influx; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.openems.backend.timedata.influx.api.InfluxImpl; + +/* + * Example JUNit test case + * + */ + +public class ProviderImplTest { + + /* + * Example test method + */ + + @Test + public void simple() { + InfluxImpl impl = new InfluxImpl(); + assertNotNull(impl); + } + +} diff --git a/io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 5e6bced0308..00000000000 --- a/io.openems.backend.timedata/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/timedata/TimedataService.java=UTF-8 -encoding//src/io/openems/backend/timedata/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/readme.md=UTF-8 From a82d8bc8131d2611b795fb3369b05cb470be0950 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 27 Jan 2018 23:01:45 +0100 Subject: [PATCH 016/156] Start implementation of websockets --- backend/pom.xml | 248 +++--- .../browserwebsocket/BrowserWebsocket.java | 34 - .../BrowserWebsocketSingleton.java | 449 ++--------- cnf/central.xml | 5 + .../backend/application/BackendApp.java | 3 +- .../.classpath | 8 + .../.gitignore | 3 + .../.project | 23 + .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 11 + .../bnd.bnd | 21 + .../readme.md | 8 + .../api/BrowserWebsocketService.java | 8 + .../browserwebsocket/api/package-info.java | 2 + .../test/.gitignore | 0 .../.classpath | 8 + .../.gitignore | 3 + .../.project | 23 + .../org.eclipse.core.resources.prefs | 7 + .../.settings/org.eclipse.jdt.core.prefs | 11 + .../bnd.bnd | 29 + .../debug.bndrun | 13 + ...enems.backend.browserwebsocket.impl.bndrun | 14 + .../readme.md | 8 + .../impl/BrowserWebsocketImpl.java | 31 + .../internal}/BackendCurrentDataWorker.java | 11 +- .../impl/internal}/BrowserSession.java | 30 +- .../impl/internal}/BrowserSessionData.java | 140 ++-- .../impl/internal}/BrowserSessionManager.java | 22 +- .../impl/internal/BrowserWebsocket.java | 382 ++++++++++ .../impl/ProviderImplTest.java | 26 + io.openems.backend.metadata.api/bnd.bnd | 3 +- .../backend/metadata/api/MetadataDevice.java | 2 +- .../backend/metadata/api/MetadataService.java | 3 +- .../backend/metadata/api/UserDevicesInfo.java | 36 + .../backend/metadata/odoo/OdooImpl.java | 29 +- io.openems.common/bnd.bnd | 11 +- .../io/openems/common/session/Session.java | 56 +- .../openems/common/session/SessionData.java | 14 +- .../common/session/SessionManager.java | 252 +++---- .../src/io/openems}/common/types/Device.java | 2 +- .../io/openems}/common/types/DeviceImpl.java | 2 +- .../io/openems/common/types/FieldValue.java | 58 +- .../openems/common/types/NullFieldValue.java | 58 +- .../common/types/NumberFieldValue.java | 58 +- .../common/types/StringFieldValue.java | 58 +- .../common/types/TimestampedFieldValue.java | 0 .../common/utils/SecureRandomSingleton.java | 72 +- .../io/openems/common/utils/StringUtils.java | 0 .../websocket/AbstractWebsocketServer.java | 1 - .../common/websocket/CurrentDataWorker.java | 0 .../common/websocket/DefaultMessages.java | 714 +++++++++--------- .../common/websocket/LogBehaviour.java | 0 .../common/websocket/Notification.java | 0 .../common/websocket/NotificationType.java | 50 +- .../common/websocket/WebSocketUtils.java | 226 +++--- .../common/websocket/package-info.java | 2 + 57 files changed, 1795 insertions(+), 1499 deletions(-) delete mode 100644 backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocket.java create mode 100644 io.openems.backend.browserwebsocket.api/.classpath create mode 100644 io.openems.backend.browserwebsocket.api/.gitignore create mode 100644 io.openems.backend.browserwebsocket.api/.project create mode 100644 io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.browserwebsocket.api/bnd.bnd create mode 100644 io.openems.backend.browserwebsocket.api/readme.md create mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java create mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java create mode 100644 io.openems.backend.browserwebsocket.api/test/.gitignore create mode 100644 io.openems.backend.browserwebsocket.impl/.classpath create mode 100644 io.openems.backend.browserwebsocket.impl/.gitignore create mode 100644 io.openems.backend.browserwebsocket.impl/.project create mode 100644 io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.browserwebsocket.impl/bnd.bnd create mode 100644 io.openems.backend.browserwebsocket.impl/debug.bndrun create mode 100644 io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun create mode 100644 io.openems.backend.browserwebsocket.impl/readme.md create mode 100644 io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java rename {backend/src/main/java/io/openems/backend/browserwebsocket/session => io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal}/BackendCurrentDataWorker.java (79%) rename {backend/src/main/java/io/openems/backend/browserwebsocket/session => io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal}/BrowserSession.java (84%) rename {backend/src/main/java/io/openems/backend/browserwebsocket/session => io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal}/BrowserSessionData.java (93%) rename {backend/src/main/java/io/openems/backend/browserwebsocket/session => io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal}/BrowserSessionManager.java (81%) create mode 100644 io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java create mode 100644 io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java rename {common => io.openems.common}/src/io/openems/common/session/Session.java (94%) rename {common => io.openems.common}/src/io/openems/common/session/SessionData.java (95%) rename {common => io.openems.common}/src/io/openems/common/session/SessionManager.java (96%) rename {io.openems.backend.common/src/io/openems/backend => io.openems.common/src/io/openems}/common/types/Device.java (92%) rename {io.openems.backend.common/src/io/openems/backend => io.openems.common/src/io/openems}/common/types/DeviceImpl.java (96%) rename {common => io.openems.common}/src/io/openems/common/types/FieldValue.java (97%) rename {common => io.openems.common}/src/io/openems/common/types/NullFieldValue.java (97%) rename {common => io.openems.common}/src/io/openems/common/types/NumberFieldValue.java (97%) rename {common => io.openems.common}/src/io/openems/common/types/StringFieldValue.java (97%) rename {common => io.openems.common}/src/io/openems/common/types/TimestampedFieldValue.java (100%) rename {common => io.openems.common}/src/io/openems/common/utils/SecureRandomSingleton.java (97%) rename {common => io.openems.common}/src/io/openems/common/utils/StringUtils.java (100%) rename {common => io.openems.common}/src/io/openems/common/websocket/AbstractWebsocketServer.java (99%) rename {common => io.openems.common}/src/io/openems/common/websocket/CurrentDataWorker.java (100%) rename {common => io.openems.common}/src/io/openems/common/websocket/DefaultMessages.java (95%) rename {common => io.openems.common}/src/io/openems/common/websocket/LogBehaviour.java (100%) rename {common => io.openems.common}/src/io/openems/common/websocket/Notification.java (100%) rename {common => io.openems.common}/src/io/openems/common/websocket/NotificationType.java (97%) rename {common => io.openems.common}/src/io/openems/common/websocket/WebSocketUtils.java (97%) create mode 100644 io.openems.common/src/io/openems/common/websocket/package-info.java diff --git a/backend/pom.xml b/backend/pom.xml index e5347760e34..560d29d16f8 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -1,124 +1,124 @@ - - OpenEMS Backend - Open Source Energy Management System - http://openems.io - io.openems - edge - 1.8.0-SNAPSHOT - jar - - https://github.com/OpenEMS/openems - scm:git:git://github.com/OpenEMS/openems.git - - - UTF-8 - 2.8.2 - 23.1-jre - 2.7 - 4.8.1 - 1.2.3 - 1.8 - 1.8 - 3.1.0 - 3.0.2 - 2.3.10 - 3.0.1 - 1.7.25 - 1.3.6 - - - - maven-restlet - Public online Restlet repository - http://maven.restlet.com - - - - - io.openems - common - ${project.version} - - - com.google.code.gson - gson - ${gson.version} - - - com.google.guava - guava - ${guava.version} - - - org.influxdb - influxdb-java - ${influxdb.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - org.restlet.jse - org.restlet - ${restlet.version} - - - org.restlet.jse - org.restlet.ext.slf4j - ${restlet.version} - - - com.odoojava - odoo-java-api - ${odoo-java-api.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.java-websocket - Java-WebSocket - ${websocket.version} - - - - - - maven-jar-plugin - ${maven-jar-plugin.version} - - - default-jar - none - - - - - maven-assembly-plugin - ${maven-assembly-plugin.version} - - openems-backend - false - - jar-with-dependencies - - - - - make-assembly - package - - single - - - - - - - 4.0.0 - + + OpenEMS Backend + Open Source Energy Management System + http://openems.io + io.openems + edge + 1.8.0-SNAPSHOT + jar + + https://github.com/OpenEMS/openems + scm:git:git://github.com/OpenEMS/openems.git + + + UTF-8 + 2.8.2 + 23.1-jre + 2.7 + 4.8.1 + 1.2.3 + 1.8 + 1.8 + 3.1.0 + 3.0.2 + 2.3.10 + 3.0.1 + 1.7.25 + 1.3.6 + + + + maven-restlet + Public online Restlet repositorymusi + http://maven.restlet.com + + + + + io.openems + common + ${project.version} + + + com.google.code.gson + gson + ${gson.version} + + + com.google.guava + guava + ${guava.version} + + + org.influxdb + influxdb-java + ${influxdb.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.restlet.jse + org.restlet + ${restlet.version} + + + org.restlet.jse + org.restlet.ext.slf4j + ${restlet.version} + + + com.odoojava + odoo-java-api + ${odoo-java-api.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.java-websocket + Java-WebSocket + ${websocket.version} + + + + + + maven-jar-plugin + ${maven-jar-plugin.version} + + + default-jar + none + + + + + maven-assembly-plugin + ${maven-assembly-plugin.version} + + openems-backend + false + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + 4.0.0 + diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocket.java b/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocket.java deleted file mode 100644 index ba89f9ead34..00000000000 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocket.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.openems.backend.browserwebsocket; - -import io.openems.common.exceptions.OpenemsException; - -/** - * Provider for OpenemsWebsocketServer singleton - * - * @author stefan.feilmeier - * - */ -public class BrowserWebsocket { - - private static BrowserWebsocketSingleton instance; - - /** - * Initialize and start the Websocketserver - * - * @param port - * @throws Exception - */ - public static synchronized void initialize(int port) throws OpenemsException { - BrowserWebsocket.instance = new BrowserWebsocketSingleton(port); - BrowserWebsocket.instance.start(); - } - - /** - * Returns the singleton instance - * - * @return - */ - public static synchronized BrowserWebsocketSingleton instance() { - return BrowserWebsocket.instance; - } -} \ No newline at end of file diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java b/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java index 28565a4fea3..d80987090c3 100644 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java +++ b/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java @@ -1,401 +1,48 @@ -package io.openems.backend.browserwebsocket; - -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.handshake.ClientHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.backend.browserwebsocket.session.BackendCurrentDataWorker; -import io.openems.backend.browserwebsocket.session.BrowserSession; -import io.openems.backend.browserwebsocket.session.BrowserSessionData; -import io.openems.backend.browserwebsocket.session.BrowserSessionManager; -import io.openems.backend.metadata.Metadata; -import io.openems.backend.openemswebsocket.OpenemsWebsocket; -import io.openems.backend.openemswebsocket.OpenemsWebsocketSingleton; -import io.openems.backend.openemswebsocket.session.OpenemsSession; -import io.openems.backend.timedata.Timedata; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.session.Role; -import io.openems.common.types.Device; -import io.openems.common.types.DeviceImpl; -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.StringUtils; -import io.openems.common.websocket.AbstractWebsocketServer; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.LogBehaviour; -import io.openems.common.websocket.Notification; -import io.openems.common.websocket.WebSocketUtils; - -/** - * Handles connections from a browser. - * - * @author stefan.feilmeier - * - */ -public class BrowserWebsocketSingleton - extends AbstractWebsocketServer { - private final Logger log = LoggerFactory.getLogger(BrowserWebsocketSingleton.class); - - protected BrowserWebsocketSingleton(int port) throws OpenemsException { - super(port, new BrowserSessionManager()); - } - - /** - * Open event of websocket. Parses the Odoo "session_id" and stores it in a new Session. - */ - @Override - protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - // Prepare session information - String error = ""; - BrowserSession session = null; - Optional sessionIdOpt = Optional.empty(); - - try { - // get cookie information - JsonObject jCookie = parseCookieFromHandshake(handshake); - sessionIdOpt = JsonUtils.getAsOptionalString(jCookie, "session_id"); - - // try to get token of an existing, valid session from cookie - if (jCookie.has("token")) { - String token = JsonUtils.getAsString(jCookie, "token"); - Optional existingSessionOpt = sessionManager.getSessionByToken(token); - if (existingSessionOpt.isPresent()) { - BrowserSession existingSession = existingSessionOpt.get(); - // test if it is the same Odoo session_id - if (sessionIdOpt.equals(existingSession.getData().getOdooSessionId())) { - session = existingSession; - } - } - } - } catch (OpenemsException e) { - error = e.getMessage(); - } - - if (session == null) { - // create new session if no existing one was found - BrowserSessionData sessionData = new BrowserSessionData(); - sessionData.setOdooSessionId(sessionIdOpt); - session = sessionManager.createNewSession(sessionData); - } - - // check Odoo session and refresh info from Odoo - try { - Metadata.instance().getInfoWithSession(session); - } catch (OpenemsException e) { - error = e.getMessage(); - } - - // check if the session is now valid and send reply to browser - BrowserSessionData data = session.getData(); - if (error.isEmpty()) { - // add isOnline information - OpenemsWebsocketSingleton openemsWebsocket = OpenemsWebsocket.instance(); - for (DeviceImpl device : data.getDevices()) { - device.setOnline(openemsWebsocket.isOpenemsWebsocketConnected(device.getName())); - } - - // send connection successful to browser - JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), Optional.empty(), - data.getDevices()); - // TODO write user name to log output - WebSocketUtils.send(websocket, jReply); - - // add websocket to local cache - this.addWebsocket(websocket, session); - - log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") - + "]."); - - } else { - // send connection failed to browser - JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); - WebSocketUtils.send(websocket, jReply); - log.warn("User [" + data.getUserName() + "] connection failed. Session [" - + data.getOdooSessionId().orElse("") + "] Error [" + error + "]."); - - websocket.closeConnection(CloseFrame.REFUSE, error); - } - } - - /** - * Message event of websocket. Handles a new message. - */ - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - /* - * With existing device name - */ - if (deviceNameOpt.isPresent()) { - String deviceName = deviceNameOpt.get(); - Optional deviceIdOpt = Device.parseNumberFromName(deviceName); - /* - * Query historic data - */ - if (jMessage.has("historicData")) { - // parse deviceId - JsonArray jMessageId = jMessageIdOpt.get(); - try { - JsonObject jHistoricData = JsonUtils.getAsJsonObject(jMessage, "historicData"); - JsonObject jReply = WebSocketUtils.historicData(jMessageId, jHistoricData, deviceIdOpt, - Timedata.instance(), Role.ADMIN); - // TODO read role from device - WebSocketUtils.send(websocket, jReply); - } catch (OpenemsException e) { - log.error(e.getMessage()); - } - } - - // get session - Optional sessionOpt = this.getSessionFromWebsocket(websocket); - if (!sessionOpt.isPresent()) { - log.warn("No BrowserSession available."); - // throw new OpenemsException("No BrowserSession available."); - } - BrowserSession session = sessionOpt.get(); - - /* - * Subscribe to currentData - */ - if (jMessage.has("currentData")) { - JsonObject jCurrentData; - try { - jCurrentData = JsonUtils.getAsJsonObject(jMessage, "currentData"); - log.info("User [" + session.getData().getUserName() + "] subscribed to current data for device [" - + deviceName + "]: " + StringUtils.toShortString(jCurrentData, 50)); - JsonArray jMessageId = jMessageIdOpt.get(); - int deviceId = deviceIdOpt.get(); - this.currentData(session, websocket, jCurrentData, jMessageId, deviceName, deviceId); - } catch (OpenemsException e) { - log.error(e.getMessage()); - } - } - - /* - * Serve "Config -> Query" from cache - */ - Optional configModeOpt = Optional.empty(); - if (jMessage.has("config")) { - Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); - if (jConfigOpt.isPresent()) { - configModeOpt = JsonUtils.getAsOptionalString(jConfigOpt.get(), "mode"); - if (configModeOpt.isPresent() && configModeOpt.get().equals("query")) { - /* - * Query current config - */ - Optional openemsSessionOpt = OpenemsWebsocket.instance() - .getOpenemsSession(deviceName); - Optional openemsConfig = Optional.empty(); - if (openemsSessionOpt.isPresent()) { - openemsConfig = openemsSessionOpt.get().getData().getOpenemsConfigOpt(); - } - if (!openemsConfig.isPresent()) { - // set configMode to empty, so that the request is forwarded to Edge - configModeOpt = Optional.empty(); - } else { - log.info("User [" + session.getData().getUserName() - + "]: Sent OpenEMS-Config from cache for device [" + deviceName + "]."); - JsonObject jReply = DefaultMessages.configQueryReply(openemsConfig.get()); - if (deviceNameOpt.isPresent()) { - jReply.addProperty("device", deviceNameOpt.get()); - } - if (jMessageIdOpt.isPresent()) { - jReply.add("id", jMessageIdOpt.get()); - } - WebSocketUtils.send(websocket, jReply); - } - } - } - } - - /* - * Forward to OpenEMS Edge - */ - if ((jMessage.has("config") && !configModeOpt.orElse("").equals("query")) || jMessage.has("log") - || jMessage.has("system")) { - try { - forwardMessageToOpenems(session, websocket, jMessage, deviceName); - } catch (OpenemsException e) { - WebSocketUtils.sendNotification(websocket, new JsonArray(), LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_UNABLE_TO_FORWARD, deviceName, e.getMessage()); - } - } - } - } - - @Override - protected void _onClose(WebSocket websocket, Optional sessionOpt) { - // nothing to do. Session is kept open. - } - - /** - * Forward message to OpenEMS websocket. - * - * @throws OpenemsException - */ - private void forwardMessageToOpenems(BrowserSession session, WebSocket websocket, JsonObject jMessage, - String deviceName) throws OpenemsException { - // remove device from message - if (jMessage.has("device")) { - jMessage.remove("device"); - } - - // add session token to message id for identification - JsonArray jId; - if (jMessage.has("id")) { - jId = JsonUtils.getAsJsonArray(jMessage, "id"); - } else { - jId = new JsonArray(); - } - jId.add(session.getToken()); - jMessage.add("id", jId); - - // add authentication role - Role role = Role.GUEST; - for (DeviceImpl device : session.getData().getDevices(deviceName)) { - role = device.getRole(); - } - jMessage.addProperty("role", role.name().toLowerCase()); - - // get OpenEMS websocket and forward message - Optional openemsWebsocketOpt = OpenemsWebsocket.instance().getOpenemsWebsocket(deviceName); - if (openemsWebsocketOpt.isPresent()) { - WebSocket openemsWebsocket = openemsWebsocketOpt.get(); - if (WebSocketUtils.send(openemsWebsocket, jMessage)) { - return; - } else { - throw new OpenemsException("Sending failed"); - } - } else { - throw new OpenemsException("Device is not connected."); - } - } - - /** - * Handle current data subscriptions - * (copied from EdgeWebsocketHandler. Try to keep synced...) - * - * @param j - */ - private synchronized void currentData(BrowserSession session, WebSocket websocket, JsonObject jCurrentData, - JsonArray jId, String deviceName, int deviceId) { - try { - String mode = JsonUtils.getAsString(jCurrentData, "mode"); - - if (mode.equals("subscribe")) { - /* - * Subscribe to channels - */ - - // remove old worker if existed - Optional workerOpt = session.getData().getCurrentDataWorkerOpt(); - if (workerOpt.isPresent()) { - session.getData().setCurrentDataWorkerOpt(null); - workerOpt.get().dispose(); - } - - // parse subscribed channels - HashMultimap channels = HashMultimap.create(); - JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); - for (Entry entry : jSubscribeChannels.entrySet()) { - String thing = entry.getKey(); - JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement jChannel : jChannels) { - String channel = JsonUtils.getAsString(jChannel); - channels.put(thing, channel); - } - } - if (!channels.isEmpty()) { - // create new worker - BackendCurrentDataWorker worker = new BackendCurrentDataWorker(deviceId, deviceName, websocket, jId, - channels); - session.getData().setCurrentDataWorkerOpt(worker); - } - } - } catch (OpenemsException e) { - log.warn(e.getMessage()); - } - } - - // TODO notification handling - // /** - // * Generates a generic notification message - // * - // * @param message - // * @return - // */ - // private JsonObject generateNotification(String message) { - // JsonObject j = new JsonObject(); - // JsonObject jNotification = new JsonObject(); - // jNotification.addProperty("message", message); - // j.add("notification", jNotification); - // return j; - // } - - // TODO system command - // /** - // * System command - // * - // * @param j - // */ - // private synchronized void system(String deviceName, JsonElement jSubscribeElement) { - // JsonObject j = new JsonObject(); - // j.add("system", jSubscribeElement); - // Optional openemsWebsocketOpt = ConnectionManager.instance() - // .getOpenemsWebsocketFromDeviceName(deviceName); - // if (!openemsWebsocketOpt.isPresent()) { - // log.warn("Trying to forward system call to [" + deviceName + "], but it is not online"); - // } - // WebSocket openemsWebsocket = openemsWebsocketOpt.get(); - // log.info(deviceName + ": forward system call to OpenEMS " + StringUtils.toShortString(j, 100)); - // WebSocketUtils.send(openemsWebsocket, j); - // } - - /** - * OpenEMS Websocket tells us, when the connection to an OpenEMS Edge is closed - * - * @param name - */ - public void openemsConnectionClosed(String name) { - for (BrowserSession session : this.sessionManager.getSessions()) { - for (DeviceImpl device : session.getData().getDevices()) { - if (name.equals(device.getName())) { - Optional websocketOpt = this.getWebsocketFromSession(session); - WebSocketUtils.sendNotification(websocketOpt, new JsonArray(), LogBehaviour.DO_NOT_WRITE_TO_LOG, - Notification.EDGE_CONNECTION_ClOSED, name); - } - } - } - } - - /** - * OpenEMS Websocket tells us, when the connection to an OpenEMS Edge is openend - * - * @param name - */ - public void openemsConnectionOpened(Set names) { - for (BrowserSession session : this.sessionManager.getSessions()) { - for (DeviceImpl device : session.getData().getDevices()) { - for (String name : names) { - if (name.equals(device.getName())) { - // Optional websocketOpt = this.getWebsocketFromSession(session); - // TODO re-enable this once it is stable - // WebSocketUtils.sendNotification(websocketOpt, new JsonArray(), - // LogBehaviour.DO_NOT_WRITE_TO_LOG, - // Notification.EDGE_CONNECTION_OPENED, name); - } - } - } - } - } -} +package io.openems.backend.browserwebsocket; + +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; + +import javax.management.Notification; + +import org.java_websocket.WebSocket; +import org.java_websocket.framing.CloseFrame; +import org.java_websocket.handshake.ClientHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.HashMultimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.backend.browserwebsocket.session.BackendCurrentDataWorker; +import io.openems.backend.browserwebsocket.session.BrowserSession; +import io.openems.backend.browserwebsocket.session.BrowserSessionData; +import io.openems.backend.browserwebsocket.session.BrowserSessionManager; +import io.openems.backend.metadata.Metadata; +import io.openems.backend.openemswebsocket.OpenemsWebsocket; +import io.openems.backend.openemswebsocket.OpenemsWebsocketSingleton; +import io.openems.backend.openemswebsocket.session.OpenemsSession; +import io.openems.backend.timedata.Timedata; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.Device; +import io.openems.common.types.DeviceImpl; +import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.AbstractWebsocketServer; +import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.LogBehaviour; +import io.openems.common.websocket.WebSocketUtils; + +/** + * Handles connections from a browser. + * + * @author stefan.feilmeier + * + */ +public class BrowserWebsocketSingleton + extends AbstractWebsocketServer { + private final Logger log = LoggerFactory.getLogger(BrowserWebsocketSingleton.class); + + diff --git a/cnf/central.xml b/cnf/central.xml index fb92f0ac46a..144326d1ecc 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -61,6 +61,11 @@ org.apache.servicemix.bundles.ws-commons-util 1.0.2_2 + + org.java-websocket + Java-WebSocket + 1.3.6 + diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 20888d385ea..9dfffa7377a 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -12,6 +12,7 @@ @Component() public class BackendApp { + // TODO: use setter to not kill BackendApp all the time... @Reference MetadataService metadataService; @@ -22,7 +23,7 @@ public class BackendApp { void activate() { System.out.println("Activate BackendApp"); try { - this.metadataService.getInfoWithSession(); + this.metadataService.getInfoWithSession("8635d53109cafc9d51de443c7d2bc4e980ba1b5d"); } catch (OpenemsException e) { e.printStackTrace(); } diff --git a/io.openems.backend.browserwebsocket.api/.classpath b/io.openems.backend.browserwebsocket.api/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.browserwebsocket.api/.gitignore b/io.openems.backend.browserwebsocket.api/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.browserwebsocket.api/.project b/io.openems.backend.browserwebsocket.api/.project new file mode 100644 index 00000000000..6a7fcd32642 --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.browserwebsocket.api + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..26e809422ae --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java=UTF-8 +encoding//src/io/openems/backend/browserwebsocket/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.browserwebsocket.api/bnd.bnd b/io.openems.backend.browserwebsocket.api/bnd.bnd new file mode 100644 index 00000000000..89805b5f67f --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/bnd.bnd @@ -0,0 +1,21 @@ +# +# io.openems.backend.browserwebsocket.api DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: \ + io.openems.backend.browserwebsocket.api + +Require-Capability: \ + compile-only + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1 + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.browserwebsocket.api/readme.md b/io.openems.backend.browserwebsocket.api/readme.md new file mode 100644 index 00000000000..109f3501a3b --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.browserwebsocket.api + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java new file mode 100644 index 00000000000..a7bf79d079c --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java @@ -0,0 +1,8 @@ +package io.openems.backend.browserwebsocket.api; + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface BrowserWebsocketService { + +} diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java new file mode 100644 index 00000000000..b1bf5e6e5e2 --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.browserwebsocket.api; diff --git a/io.openems.backend.browserwebsocket.api/test/.gitignore b/io.openems.backend.browserwebsocket.api/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.backend.browserwebsocket.impl/.classpath b/io.openems.backend.browserwebsocket.impl/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.browserwebsocket.impl/.gitignore b/io.openems.backend.browserwebsocket.impl/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.browserwebsocket.impl/.project b/io.openems.backend.browserwebsocket.impl/.project new file mode 100644 index 00000000000..a3cdb5d86fd --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.browserwebsocket.impl + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..2356310f168 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java=UTF-8 +encoding//test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.browserwebsocket.impl.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.browserwebsocket.impl/bnd.bnd b/io.openems.backend.browserwebsocket.impl/bnd.bnd new file mode 100644 index 00000000000..f32ec8a9645 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/bnd.bnd @@ -0,0 +1,29 @@ +# +# io.openems.backend.browserwebsocket.impl PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: \ + io.openems.backend.browserwebsocket.impl.api;-provide=true + +Private-Package: \ + io.openems.backend.browserwebsocket.impl,\ + io.openems.backend.browserwebsocket.impl.internal + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.browserwebsocket.api;version=latest,\ + org.java-websocket:Java-WebSocket,\ + com.google.guava,\ + com.google.gson,\ + io.openems.common;version=latest,\ + io.openems.backend.timedata.api;version=latest,\ + io.openems.backend.metadata.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.browserwebsocket.impl/debug.bndrun b/io.openems.backend.browserwebsocket.impl/debug.bndrun new file mode 100644 index 00000000000..969e63e6b05 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.browserwebsocket.impl DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.browserwebsocket.impl.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun b/io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun new file mode 100644 index 00000000000..55f1e44a8c4 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun @@ -0,0 +1,14 @@ +# +# io.openems.backend.browserwebsocket.impl LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.browserwebsocket.impl.launch +JPM-Command: provider + + +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.impl.provider)' + +-runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.browserwebsocket.impl/readme.md b/io.openems.backend.browserwebsocket.impl/readme.md new file mode 100644 index 00000000000..8174d352e03 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.browserwebsocket.impl Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java new file mode 100644 index 00000000000..8f9be439a6f --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java @@ -0,0 +1,31 @@ +package io.openems.backend.browserwebsocket.impl; + +import java.util.Map; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.osgi.service.metatype.annotations.Designate; + +@Designate(ocd = BrowserWebsocketImpl.Config.class, factory = false) +@Component(name = "io.openems.backend.browserwebsocket.impl") +public class BrowserWebsocketImpl { + + @ObjectClassDefinition + @interface Config { + int port(); + } + + private int port; + + @Activate + void activate(Config config) { + this.port = config.port(); + } + + @Deactivate + void deactivate() { + } + +} diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BackendCurrentDataWorker.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BackendCurrentDataWorker.java similarity index 79% rename from backend/src/main/java/io/openems/backend/browserwebsocket/session/BackendCurrentDataWorker.java rename to io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BackendCurrentDataWorker.java index 36a45140557..de6aaa8df8e 100644 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BackendCurrentDataWorker.java +++ b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BackendCurrentDataWorker.java @@ -1,14 +1,16 @@ -package io.openems.backend.browserwebsocket.session; +package io.openems.backend.browserwebsocket.impl.internal; + import java.util.Optional; import org.java_websocket.WebSocket; +import org.osgi.service.component.annotations.Reference; import com.google.common.collect.HashMultimap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; -import io.openems.backend.timedata.Timedata; +import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.NotImplementedException; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; @@ -16,6 +18,9 @@ public class BackendCurrentDataWorker extends CurrentDataWorker { + @Reference + TimedataService timedata; + private final WebSocket websocket; private final int deviceId; @@ -28,7 +33,7 @@ public BackendCurrentDataWorker(int deviceId, String deviceName, WebSocket webso @Override protected Optional getChannelValue(ChannelAddress channelAddress) { - Optional channelCacheOpt = Timedata.instance().getChannelValue(this.deviceId, channelAddress); + Optional channelCacheOpt = timedata.getChannelValue(this.deviceId, channelAddress); if (channelCacheOpt.isPresent()) { try { return Optional.ofNullable(JsonUtils.getAsJsonElement(channelCacheOpt.get())); diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSession.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSession.java similarity index 84% rename from backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSession.java rename to io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSession.java index 7d18ac31b3a..143d7c3da5b 100644 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSession.java +++ b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSession.java @@ -1,15 +1,15 @@ -package io.openems.backend.browserwebsocket.session; - -import io.openems.common.session.Session; - -public class BrowserSession extends Session { - - protected BrowserSession(String token, BrowserSessionData data) { - super(token, data); - } - - @Override - public String toString() { - return "User [" + getData().getUserName() + "] Session [" + getData().getOdooSessionId().orElse("") + "]"; - } -} +package io.openems.backend.browserwebsocket.impl.internal; + +import io.openems.common.session.Session; + +public class BrowserSession extends Session { + + protected BrowserSession(String token, BrowserSessionData data) { + super(token, data); + } + + @Override + public String toString() { + return "User [" + getData().getUserName() + "] Session [" + getData().getOdooSessionId().orElse("") + "]"; + } +} diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSessionData.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionData.java similarity index 93% rename from backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSessionData.java rename to io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionData.java index eb4e9e45e7d..f1891a206da 100644 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSessionData.java +++ b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionData.java @@ -1,70 +1,70 @@ -package io.openems.backend.browserwebsocket.session; - -import java.util.Collection; -import java.util.Optional; -import java.util.Set; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.gson.JsonObject; - -import io.openems.common.session.SessionData; -import io.openems.common.types.DeviceImpl; - -public class BrowserSessionData extends SessionData { - private String userName = ""; - private Optional userId = Optional.empty(); - private Optional odooSessionIdOpt = Optional.empty(); - private LinkedHashMultimap devices = LinkedHashMultimap.create(); - private Optional currentDataWorkerOpt = Optional.empty(); - - public Optional getOdooSessionId() { - return odooSessionIdOpt; - } - - public void setOdooSessionId(Optional odooSessionIdOpt) { - this.odooSessionIdOpt = odooSessionIdOpt; - } - - public void setDevices(LinkedHashMultimap deviceMap) { - this.devices = deviceMap; - } - - public void setUserId(Integer userId) { - this.userId = Optional.of(userId); - } - - public void setUserName(String name) { - this.userName = name; - } - - public Optional getUserId() { - return userId; - } - - public String getUserName() { - return userName; - } - - public Set getDevices(String name) { - return this.devices.get(name); - } - - public Collection getDevices() { - return this.devices.values(); - } - - public void setCurrentDataWorkerOpt(BackendCurrentDataWorker currentDataWorker) { - this.currentDataWorkerOpt = Optional.ofNullable(currentDataWorker); - } - - public Optional getCurrentDataWorkerOpt() { - return currentDataWorkerOpt; - } - - @Override - public JsonObject toJsonObject() { - JsonObject j = new JsonObject(); - // TODO Auto-generated method stub - return j; - } -} +package io.openems.backend.browserwebsocket.impl.internal; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.gson.JsonObject; + +import io.openems.common.session.SessionData; +import io.openems.common.types.DeviceImpl; + +public class BrowserSessionData extends SessionData { + private String userName = ""; + private Optional userId = Optional.empty(); + private Optional odooSessionIdOpt = Optional.empty(); + private LinkedHashMultimap devices = LinkedHashMultimap.create(); + private Optional currentDataWorkerOpt = Optional.empty(); + + public Optional getOdooSessionId() { + return odooSessionIdOpt; + } + + public void setOdooSessionId(Optional odooSessionIdOpt) { + this.odooSessionIdOpt = odooSessionIdOpt; + } + + public void setDevices(LinkedHashMultimap deviceMap) { + this.devices = deviceMap; + } + + public void setUserId(Integer userId) { + this.userId = Optional.of(userId); + } + + public void setUserName(String name) { + this.userName = name; + } + + public Optional getUserId() { + return userId; + } + + public String getUserName() { + return userName; + } + + public Set getDevices(String name) { + return this.devices.get(name); + } + + public Collection getDevices() { + return this.devices.values(); + } + + public void setCurrentDataWorkerOpt(BackendCurrentDataWorker currentDataWorker) { + this.currentDataWorkerOpt = Optional.ofNullable(currentDataWorker); + } + + public Optional getCurrentDataWorkerOpt() { + return currentDataWorkerOpt; + } + + @Override + public JsonObject toJsonObject() { + JsonObject j = new JsonObject(); + // TODO Auto-generated method stub + return j; + } +} diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSessionManager.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionManager.java similarity index 81% rename from backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSessionManager.java rename to io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionManager.java index b91f4ce04a8..208c1b53e17 100644 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/session/BrowserSessionManager.java +++ b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionManager.java @@ -1,11 +1,11 @@ -package io.openems.backend.browserwebsocket.session; - -import io.openems.common.session.SessionManager; - -public class BrowserSessionManager extends SessionManager { - - @Override - public BrowserSession _createNewSession(String token, BrowserSessionData data) { - return new BrowserSession(token, data); - } -} +package io.openems.backend.browserwebsocket.impl.internal; + +import io.openems.common.session.SessionManager; + +public class BrowserSessionManager extends SessionManager { + + @Override + public BrowserSession _createNewSession(String token, BrowserSessionData data) { + return new BrowserSession(token, data); + } +} diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java new file mode 100644 index 00000000000..23f7220b4aa --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java @@ -0,0 +1,382 @@ +package io.openems.backend.browserwebsocket.impl.internal; + +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; + +import com.google.gson.JsonObject; + +import io.openems.backend.metadata.api.MetadataService; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.AbstractWebsocketServer; + +public class BrowserWebsocket + extends AbstractWebsocketServer { + + private final MetadataService metadata; + + protected BrowserWebsocket(int port, MetadataService metadata) throws OpenemsException { + super(port, new BrowserSessionManager()); + this.metadata = metadata; + } + + /** + * Open event of websocket. Parses the Odoo "session_id" and stores it in a new + * Session. + */ + @Override + protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + // Prepare session information + String error = ""; + BrowserSession session = null; + Optional sessionIdOpt = Optional.empty(); + + try { + // get cookie information + JsonObject jCookie = parseCookieFromHandshake(handshake); + sessionIdOpt = JsonUtils.getAsOptionalString(jCookie, "session_id"); + + // try to get token of an existing, valid session from cookie + if (jCookie.has("token")) { + String token = JsonUtils.getAsString(jCookie, "token"); + Optional existingSessionOpt = sessionManager.getSessionByToken(token); + if (existingSessionOpt.isPresent()) { + BrowserSession existingSession = existingSessionOpt.get(); + // test if it is the same Odoo session_id + if (sessionIdOpt.equals(existingSession.getData().getOdooSessionId())) { + session = existingSession; + } + } + } + } catch (OpenemsException e) { + error = e.getMessage(); + } + + if (session == null) { + // create new session if no existing one was found + BrowserSessionData sessionData = new BrowserSessionData(); + sessionData.setOdooSessionId(sessionIdOpt); + session = sessionManager.createNewSession(sessionData); + } + + // check if the session is now valid and send reply to browser + BrowserSessionData data = session.getData(); + + // check Odoo session and refresh info from Odoo + if (!data.getOdooSessionId().isPresent()) { + error = "Session-ID is missing."; + } else { + try { + this.metadata.getInfoWithSession(session.getData().getOdooSessionId().get()); + } catch (OpenemsException e) { + error = e.getMessage(); + } + } + + if (error.isEmpty()) { + // add isOnline information + OpenemsWebsocketSingleton openemsWebsocket = OpenemsWebsocket.instance(); + for (DeviceImpl device : data.getDevices()) { + device.setOnline(openemsWebsocket.isOpenemsWebsocketConnected(device.getName())); + } + + // send connection successful to browser + JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), Optional.empty(), + data.getDevices()); + // TODO write user name to log output + WebSocketUtils.send(websocket, jReply); + + // add websocket to local cache + this.addWebsocket(websocket, session); + + log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") + + "]."); + + } else { + // send connection failed to browser + JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); + WebSocketUtils.send(websocket, jReply); + log.warn("User [" + data.getUserName() + "] connection failed. Session [" + + data.getOdooSessionId().orElse("") + "] Error [" + error + "]."); + + websocket.closeConnection(CloseFrame.REFUSE, error); + } + } + + /** + * Message event of websocket. Handles a new message. + */ + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, + Optional deviceNameOpt) { + /* + * With existing device name + */ + if (deviceNameOpt.isPresent()) { + String deviceName = deviceNameOpt.get(); + Optional deviceIdOpt = Device.parseNumberFromName(deviceName); + /* + * Query historic data + */ + if (jMessage.has("historicData")) { + // parse deviceId + JsonArray jMessageId = jMessageIdOpt.get(); + try { + JsonObject jHistoricData = JsonUtils.getAsJsonObject(jMessage, "historicData"); + JsonObject jReply = WebSocketUtils.historicData(jMessageId, jHistoricData, deviceIdOpt, + Timedata.instance(), Role.ADMIN); + // TODO read role from device + WebSocketUtils.send(websocket, jReply); + } catch (OpenemsException e) { + log.error(e.getMessage()); + } + } + + // get session + Optional sessionOpt = this.getSessionFromWebsocket(websocket); + if (!sessionOpt.isPresent()) { + log.warn("No BrowserSession available."); + // throw new OpenemsException("No BrowserSession available."); + } + BrowserSession session = sessionOpt.get(); + + /* + * Subscribe to currentData + */ + if (jMessage.has("currentData")) { + JsonObject jCurrentData; + try { + jCurrentData = JsonUtils.getAsJsonObject(jMessage, "currentData"); + log.info("User [" + session.getData().getUserName() + "] subscribed to current data for device [" + + deviceName + "]: " + StringUtils.toShortString(jCurrentData, 50)); + JsonArray jMessageId = jMessageIdOpt.get(); + int deviceId = deviceIdOpt.get(); + this.currentData(session, websocket, jCurrentData, jMessageId, deviceName, deviceId); + } catch (OpenemsException e) { + log.error(e.getMessage()); + } + } + + /* + * Serve "Config -> Query" from cache + */ + Optional configModeOpt = Optional.empty(); + if (jMessage.has("config")) { + Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); + if (jConfigOpt.isPresent()) { + configModeOpt = JsonUtils.getAsOptionalString(jConfigOpt.get(), "mode"); + if (configModeOpt.isPresent() && configModeOpt.get().equals("query")) { + /* + * Query current config + */ + Optional openemsSessionOpt = OpenemsWebsocket.instance() + .getOpenemsSession(deviceName); + Optional openemsConfig = Optional.empty(); + if (openemsSessionOpt.isPresent()) { + openemsConfig = openemsSessionOpt.get().getData().getOpenemsConfigOpt(); + } + if (!openemsConfig.isPresent()) { + // set configMode to empty, so that the request is forwarded to Edge + configModeOpt = Optional.empty(); + } else { + log.info("User [" + session.getData().getUserName() + + "]: Sent OpenEMS-Config from cache for device [" + deviceName + "]."); + JsonObject jReply = DefaultMessages.configQueryReply(openemsConfig.get()); + if (deviceNameOpt.isPresent()) { + jReply.addProperty("device", deviceNameOpt.get()); + } + if (jMessageIdOpt.isPresent()) { + jReply.add("id", jMessageIdOpt.get()); + } + WebSocketUtils.send(websocket, jReply); + } + } + } + } + + /* + * Forward to OpenEMS Edge + */ + if ((jMessage.has("config") && !configModeOpt.orElse("").equals("query")) || jMessage.has("log") + || jMessage.has("system")) { + try { + forwardMessageToOpenems(session, websocket, jMessage, deviceName); + } catch (OpenemsException e) { + WebSocketUtils.sendNotification(websocket, new JsonArray(), LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_UNABLE_TO_FORWARD, deviceName, e.getMessage()); + } + } + } + } + + @Override + protected void _onClose(WebSocket websocket, Optional sessionOpt) { + // nothing to do. Session is kept open. + } + + /** + * Forward message to OpenEMS websocket. + * + * @throws OpenemsException + */ + private void forwardMessageToOpenems(BrowserSession session, WebSocket websocket, JsonObject jMessage, + String deviceName) throws OpenemsException { + // remove device from message + if (jMessage.has("device")) { + jMessage.remove("device"); + } + + // add session token to message id for identification + JsonArray jId; + if (jMessage.has("id")) { + jId = JsonUtils.getAsJsonArray(jMessage, "id"); + } else { + jId = new JsonArray(); + } + jId.add(session.getToken()); + jMessage.add("id", jId); + + // add authentication role + Role role = Role.GUEST; + for (DeviceImpl device : session.getData().getDevices(deviceName)) { + role = device.getRole(); + } + jMessage.addProperty("role", role.name().toLowerCase()); + + // get OpenEMS websocket and forward message + Optional openemsWebsocketOpt = OpenemsWebsocket.instance().getOpenemsWebsocket(deviceName); + if (openemsWebsocketOpt.isPresent()) { + WebSocket openemsWebsocket = openemsWebsocketOpt.get(); + if (WebSocketUtils.send(openemsWebsocket, jMessage)) { + return; + } else { + throw new OpenemsException("Sending failed"); + } + } else { + throw new OpenemsException("Device is not connected."); + } + } + + /** + * Handle current data subscriptions (copied from EdgeWebsocketHandler. Try to + * keep synced...) + * + * @param j + */ + private synchronized void currentData(BrowserSession session, WebSocket websocket, JsonObject jCurrentData, + JsonArray jId, String deviceName, int deviceId) { + try { + String mode = JsonUtils.getAsString(jCurrentData, "mode"); + + if (mode.equals("subscribe")) { + /* + * Subscribe to channels + */ + + // remove old worker if existed + Optional workerOpt = session.getData().getCurrentDataWorkerOpt(); + if (workerOpt.isPresent()) { + session.getData().setCurrentDataWorkerOpt(null); + workerOpt.get().dispose(); + } + + // parse subscribed channels + HashMultimap channels = HashMultimap.create(); + JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); + for (Entry entry : jSubscribeChannels.entrySet()) { + String thing = entry.getKey(); + JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement jChannel : jChannels) { + String channel = JsonUtils.getAsString(jChannel); + channels.put(thing, channel); + } + } + if (!channels.isEmpty()) { + // create new worker + BackendCurrentDataWorker worker = new BackendCurrentDataWorker(deviceId, deviceName, websocket, jId, + channels); + session.getData().setCurrentDataWorkerOpt(worker); + } + } + } catch (OpenemsException e) { + log.warn(e.getMessage()); + } + } + + // TODO notification handling + // /** + // * Generates a generic notification message + // * + // * @param message + // * @return + // */ + // private JsonObject generateNotification(String message) { + // JsonObject j = new JsonObject(); + // JsonObject jNotification = new JsonObject(); + // jNotification.addProperty("message", message); + // j.add("notification", jNotification); + // return j; + // } + + // TODO system command + // /** + // * System command + // * + // * @param j + // */ + // private synchronized void system(String deviceName, JsonElement + // jSubscribeElement) { + // JsonObject j = new JsonObject(); + // j.add("system", jSubscribeElement); + // Optional openemsWebsocketOpt = ConnectionManager.instance() + // .getOpenemsWebsocketFromDeviceName(deviceName); + // if (!openemsWebsocketOpt.isPresent()) { + // log.warn("Trying to forward system call to [" + deviceName + "], but it is + // not online"); + // } + // WebSocket openemsWebsocket = openemsWebsocketOpt.get(); + // log.info(deviceName + ": forward system call to OpenEMS " + + // StringUtils.toShortString(j, 100)); + // WebSocketUtils.send(openemsWebsocket, j); + // } + + /** + * OpenEMS Websocket tells us, when the connection to an OpenEMS Edge is closed + * + * @param name + */ + public void openemsConnectionClosed(String name) { + for (BrowserSession session : this.sessionManager.getSessions()) { + for (DeviceImpl device : session.getData().getDevices()) { + if (name.equals(device.getName())) { + Optional websocketOpt = this.getWebsocketFromSession(session); + WebSocketUtils.sendNotification(websocketOpt, new JsonArray(), LogBehaviour.DO_NOT_WRITE_TO_LOG, + Notification.EDGE_CONNECTION_ClOSED, name); + } + } + } + } + + /** + * OpenEMS Websocket tells us, when the connection to an OpenEMS Edge is openend + * + * @param name + */ + public void openemsConnectionOpened(Set names) { + for (BrowserSession session : this.sessionManager.getSessions()) { + for (DeviceImpl device : session.getData().getDevices()) { + for (String name : names) { + if (name.equals(device.getName())) { + // Optional websocketOpt = this.getWebsocketFromSession(session); + // TODO re-enable this once it is stable + // WebSocketUtils.sendNotification(websocketOpt, new JsonArray(), + // LogBehaviour.DO_NOT_WRITE_TO_LOG, + // Notification.EDGE_CONNECTION_OPENED, name); + } + } + } + } + } +} diff --git a/io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java b/io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java new file mode 100644 index 00000000000..28aafc2d6e4 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java @@ -0,0 +1,26 @@ +package io.openems.backend.browserwebsocket.impl; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.openems.backend.browserwebsocket.impl.BrowserWebsocketImpl; + +/* + * Example JUNit test case + * + */ + +public class ProviderImplTest { + + /* + * Example test method + */ + + @Test + public void simple() { + BrowserWebsocketImpl impl = new BrowserWebsocketImpl(); + assertNotNull(impl); + } + +} diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd index f14d9e2c2db..6fb257cb03c 100644 --- a/io.openems.backend.metadata.api/bnd.bnd +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -14,7 +14,8 @@ Export-Package: \ osgi.enroute.base.api;version=2.1,\ io.openems.common;version=latest,\ io.openems.backend.common;version=latest,\ - com.google.gson + com.google.gson,\ + com.google.guava -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java index 2214f52bd3e..014c00db115 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java @@ -4,8 +4,8 @@ import com.google.gson.JsonObject; -import io.openems.backend.common.types.Device; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.Device; public interface MetadataDevice extends Device { diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 43e82f80562..6db9642f3a3 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -3,10 +3,11 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Session; @ProviderType public interface MetadataService { - public void getInfoWithSession() throws OpenemsException; + public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsException; } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java new file mode 100644 index 00000000000..6318dd454e8 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java @@ -0,0 +1,36 @@ +package io.openems.backend.metadata.api; + +import com.google.common.collect.Multimap; + +import io.openems.common.types.DeviceImpl; + +public class UserDevicesInfo { + + private int userId; + private String userName; + private Multimap deviceMap; + + public void setUserId(int userId) { + this.userId = userId; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setDevices(Multimap deviceMap) { + this.deviceMap = deviceMap; + } + + public int getUserId() { + return userId; + } + + public String getUserName() { + return userName; + } + + public Multimap getDeviceMap() { + return deviceMap; + } +} diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java index bdffc3fbe46..637029390e5 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java @@ -6,9 +6,10 @@ import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; -import io.openems.backend.common.types.DeviceImpl; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.metadata.api.UserDevicesInfo; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.DeviceImpl; import io.openems.common.utils.JsonUtils; import java.io.BufferedReader; @@ -70,22 +71,9 @@ void deactivate() { * @throws OpenemsException */ @Override - // public void getInfoWithSession(BrowserSession session) throws OpenemsException { - public void getInfoWithSession() throws OpenemsException { + public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsException { HttpURLConnection connection = null; try { - // get session_id from Session - // SessionData sessionData = session.getData(); - // if (!(sessionData instanceof BrowserSessionData)) { - // throw new OpenemsException("Session is of wrong type."); - // } - // BrowserSessionData data = (BrowserSessionData) sessionData; - // if (!(data.getOdooSessionId().isPresent())) { - // throw new OpenemsException("Session-ID is missing."); - // } - // String sessionId = data.getOdooSessionId().get(); - String sessionId = "8635d53109cafc9d51de443c7d2bc4e980ba1b5d"; - // send request to Odoo String charset = "US-ASCII"; String query = String.format("session_id=%s", URLEncoder.encode(sessionId, charset)); @@ -114,11 +102,13 @@ public void getInfoWithSession() throws OpenemsException { } if (j.has("result")) { + UserDevicesInfo info = new UserDevicesInfo(); + // parse the result JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); -// data.setUserId(JsonUtils.getAsInt(jUser, "id")); -// data.setUserName(JsonUtils.getAsString(jUser, "name")); + info.setUserId(JsonUtils.getAsInt(jUser, "id")); + info.setUserName(JsonUtils.getAsString(jUser, "name")); JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); for (JsonElement jDevice : jDevices) { @@ -129,9 +119,8 @@ public void getInfoWithSession() throws OpenemsException { JsonUtils.getAsString(jDevice, "producttype"), // JsonUtils.getAsString(jDevice, "role"))); } - System.out.println(deviceMap); -// data.setDevices(deviceMap); - return; + info.setDevices(deviceMap); + return info; } } } catch (IOException e) { diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd index 5dc439927f8..4dc256c37c6 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -3,23 +3,22 @@ # Bundle-Version: 1.0.0.${tstamp} -Bundle-Description: \ - This project contains a complete example with API, provider, and JUnit test code. \ - \ - ${warning;Please update this Bundle-Description in bnd.bnd} Export-Package: \ io.openems.common.session,\ io.openems.common.utils,\ io.openems.common.exceptions,\ io.openems.common.types,\ - io.openems.common.api + io.openems.common.api,\ + io.openems.common.websocket -includeresource: {readme.md} -buildpath: \ osgi.enroute.base.api;version=2.1,\ - com.google.gson + com.google.gson,\ + org.java-websocket:Java-WebSocket,\ + com.google.guava -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/common/src/io/openems/common/session/Session.java b/io.openems.common/src/io/openems/common/session/Session.java similarity index 94% rename from common/src/io/openems/common/session/Session.java rename to io.openems.common/src/io/openems/common/session/Session.java index f4fded80fac..829266059d7 100644 --- a/common/src/io/openems/common/session/Session.java +++ b/io.openems.common/src/io/openems/common/session/Session.java @@ -1,28 +1,28 @@ -package io.openems.common.session; - -public class Session { - private final String token; - - /** - * store additional metadata to this session - */ - private final D data; - - protected Session(String token, D data) { - this.token = token; - this.data = data; - } - - public String getToken() { - return token; - } - - public D getData() { - return data; - } - - @Override - public String toString() { - return "Session [token=" + token + ", data=" + data + "]"; - } -} +package io.openems.common.session; + +public class Session { + private final String token; + + /** + * store additional metadata to this session + */ + private final D data; + + protected Session(String token, D data) { + this.token = token; + this.data = data; + } + + public String getToken() { + return token; + } + + public D getData() { + return data; + } + + @Override + public String toString() { + return "Session [token=" + token + ", data=" + data + "]"; + } +} diff --git a/common/src/io/openems/common/session/SessionData.java b/io.openems.common/src/io/openems/common/session/SessionData.java similarity index 95% rename from common/src/io/openems/common/session/SessionData.java rename to io.openems.common/src/io/openems/common/session/SessionData.java index c6e462e0205..bd2b6b1aff6 100644 --- a/common/src/io/openems/common/session/SessionData.java +++ b/io.openems.common/src/io/openems/common/session/SessionData.java @@ -1,7 +1,7 @@ -package io.openems.common.session; - -import com.google.gson.JsonObject; - -public abstract class SessionData { - public abstract JsonObject toJsonObject(); -} +package io.openems.common.session; + +import com.google.gson.JsonObject; + +public abstract class SessionData { + public abstract JsonObject toJsonObject(); +} diff --git a/common/src/io/openems/common/session/SessionManager.java b/io.openems.common/src/io/openems/common/session/SessionManager.java similarity index 96% rename from common/src/io/openems/common/session/SessionManager.java rename to io.openems.common/src/io/openems/common/session/SessionManager.java index 3550efb1c39..2452d5f5224 100644 --- a/common/src/io/openems/common/session/SessionManager.java +++ b/io.openems.common/src/io/openems/common/session/SessionManager.java @@ -1,126 +1,126 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.session; - -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.utils.SecureRandomSingleton; - -public abstract class SessionManager, D extends SessionData> { - - private final Logger log = LoggerFactory.getLogger(SessionManager.class); - private final static int SESSION_ID_LENGTH = 130; - - // TODO: invalidate old sessions in separate thread: call _removeSession to do - // so - private final Map sessions = new ConcurrentHashMap<>(); - - protected SessionManager() { - } - - public S createNewSession(String token, D data) { - S session = this._createNewSession(token, data); - this._putSession(token, session); - return session; - } - - public S createNewSession(D data) { - String token = this.generateToken(); - return this.createNewSession(token, data); - } - - public Optional getSessionByToken(String token) { - synchronized (this.sessions) { - return Optional.ofNullable(this.sessions.get(token)); - } - } - - public void removeSession(String token) { - synchronized (this.sessions) { - S session = this.sessions.get(token); - if (session != null) { - this._removeSession(token); - } - } - } - - public void removeSession(Session session) { - this.removeSession(session.getToken()); - } - - protected String generateToken() { - // Source: http://stackoverflow.com/a/41156 - SecureRandom sr = SecureRandomSingleton.getInstance(); - return new BigInteger(SESSION_ID_LENGTH, sr).toString(32); - } - - public Collection getSessions() { - return Collections.unmodifiableCollection(this.sessions.values()); - } - - /* - * Those methods are prone to be overwritten by inheritance - */ - /** - * Replies a Session object of type T - * - * @param token - * @param websocket - * @param data - * @return - */ - protected abstract S _createNewSession(String token, D data); - - /** - * This method is always called when adding a session to local database - * - * @param token - * @param session - */ - protected void _putSession(String token, S session) { - synchronized (this.sessions) { - if (this.sessions.containsKey(token)) { - log.warn("Session with token [" + token + "] already existed. Replacing with session [" + session + "]"); - } - this.sessions.put(token, session); - } - } - - /** - * This method is always called when removing a session from local database - * - * @param session - */ - protected void _removeSession(String token) { - synchronized (this.sessions) { - this.sessions.remove(token); - } - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.session; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.utils.SecureRandomSingleton; + +public abstract class SessionManager, D extends SessionData> { + + private final Logger log = LoggerFactory.getLogger(SessionManager.class); + private final static int SESSION_ID_LENGTH = 130; + + // TODO: invalidate old sessions in separate thread: call _removeSession to do + // so + private final Map sessions = new ConcurrentHashMap<>(); + + protected SessionManager() { + } + + public S createNewSession(String token, D data) { + S session = this._createNewSession(token, data); + this._putSession(token, session); + return session; + } + + public S createNewSession(D data) { + String token = this.generateToken(); + return this.createNewSession(token, data); + } + + public Optional getSessionByToken(String token) { + synchronized (this.sessions) { + return Optional.ofNullable(this.sessions.get(token)); + } + } + + public void removeSession(String token) { + synchronized (this.sessions) { + S session = this.sessions.get(token); + if (session != null) { + this._removeSession(token); + } + } + } + + public void removeSession(Session session) { + this.removeSession(session.getToken()); + } + + protected String generateToken() { + // Source: http://stackoverflow.com/a/41156 + SecureRandom sr = SecureRandomSingleton.getInstance(); + return new BigInteger(SESSION_ID_LENGTH, sr).toString(32); + } + + public Collection getSessions() { + return Collections.unmodifiableCollection(this.sessions.values()); + } + + /* + * Those methods are prone to be overwritten by inheritance + */ + /** + * Replies a Session object of type T + * + * @param token + * @param websocket + * @param data + * @return + */ + protected abstract S _createNewSession(String token, D data); + + /** + * This method is always called when adding a session to local database + * + * @param token + * @param session + */ + protected void _putSession(String token, S session) { + synchronized (this.sessions) { + if (this.sessions.containsKey(token)) { + log.warn("Session with token [" + token + "] already existed. Replacing with session [" + session + "]"); + } + this.sessions.put(token, session); + } + } + + /** + * This method is always called when removing a session from local database + * + * @param session + */ + protected void _removeSession(String token) { + synchronized (this.sessions) { + this.sessions.remove(token); + } + } +} diff --git a/io.openems.backend.common/src/io/openems/backend/common/types/Device.java b/io.openems.common/src/io/openems/common/types/Device.java similarity index 92% rename from io.openems.backend.common/src/io/openems/backend/common/types/Device.java rename to io.openems.common/src/io/openems/common/types/Device.java index 3e16beb87ed..1575f156517 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/types/Device.java +++ b/io.openems.common/src/io/openems/common/types/Device.java @@ -1,4 +1,4 @@ -package io.openems.backend.common.types; +package io.openems.common.types; import java.util.Optional; import java.util.regex.Matcher; diff --git a/io.openems.backend.common/src/io/openems/backend/common/types/DeviceImpl.java b/io.openems.common/src/io/openems/common/types/DeviceImpl.java similarity index 96% rename from io.openems.backend.common/src/io/openems/backend/common/types/DeviceImpl.java rename to io.openems.common/src/io/openems/common/types/DeviceImpl.java index 89b933e0aee..124d8e721a9 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/types/DeviceImpl.java +++ b/io.openems.common/src/io/openems/common/types/DeviceImpl.java @@ -1,4 +1,4 @@ -package io.openems.backend.common.types; +package io.openems.common.types; import java.util.Optional; diff --git a/common/src/io/openems/common/types/FieldValue.java b/io.openems.common/src/io/openems/common/types/FieldValue.java similarity index 97% rename from common/src/io/openems/common/types/FieldValue.java rename to io.openems.common/src/io/openems/common/types/FieldValue.java index 6192220d38d..aac1c08db2e 100644 --- a/common/src/io/openems/common/types/FieldValue.java +++ b/io.openems.common/src/io/openems/common/types/FieldValue.java @@ -1,29 +1,29 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public abstract class FieldValue { - public final T value; - - public FieldValue(T value) { - this.value = value; - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public abstract class FieldValue { + public final T value; + + public FieldValue(T value) { + this.value = value; + } +} diff --git a/common/src/io/openems/common/types/NullFieldValue.java b/io.openems.common/src/io/openems/common/types/NullFieldValue.java similarity index 97% rename from common/src/io/openems/common/types/NullFieldValue.java rename to io.openems.common/src/io/openems/common/types/NullFieldValue.java index a246b1a7b88..3e889aa8636 100644 --- a/common/src/io/openems/common/types/NullFieldValue.java +++ b/io.openems.common/src/io/openems/common/types/NullFieldValue.java @@ -1,29 +1,29 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public class NullFieldValue extends FieldValue { - - public NullFieldValue() { - super(null); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public class NullFieldValue extends FieldValue { + + public NullFieldValue() { + super(null); + } + +} diff --git a/common/src/io/openems/common/types/NumberFieldValue.java b/io.openems.common/src/io/openems/common/types/NumberFieldValue.java similarity index 97% rename from common/src/io/openems/common/types/NumberFieldValue.java rename to io.openems.common/src/io/openems/common/types/NumberFieldValue.java index e2bec700b62..355804fc9a9 100644 --- a/common/src/io/openems/common/types/NumberFieldValue.java +++ b/io.openems.common/src/io/openems/common/types/NumberFieldValue.java @@ -1,29 +1,29 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public class NumberFieldValue extends FieldValue { - - public NumberFieldValue(Number value) { - super(value); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public class NumberFieldValue extends FieldValue { + + public NumberFieldValue(Number value) { + super(value); + } + +} diff --git a/common/src/io/openems/common/types/StringFieldValue.java b/io.openems.common/src/io/openems/common/types/StringFieldValue.java similarity index 97% rename from common/src/io/openems/common/types/StringFieldValue.java rename to io.openems.common/src/io/openems/common/types/StringFieldValue.java index 3cd5619c7fe..9ed25e24d93 100644 --- a/common/src/io/openems/common/types/StringFieldValue.java +++ b/io.openems.common/src/io/openems/common/types/StringFieldValue.java @@ -1,29 +1,29 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public class StringFieldValue extends FieldValue { - - public StringFieldValue(String value) { - super(value); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public class StringFieldValue extends FieldValue { + + public StringFieldValue(String value) { + super(value); + } + +} diff --git a/common/src/io/openems/common/types/TimestampedFieldValue.java b/io.openems.common/src/io/openems/common/types/TimestampedFieldValue.java similarity index 100% rename from common/src/io/openems/common/types/TimestampedFieldValue.java rename to io.openems.common/src/io/openems/common/types/TimestampedFieldValue.java diff --git a/common/src/io/openems/common/utils/SecureRandomSingleton.java b/io.openems.common/src/io/openems/common/utils/SecureRandomSingleton.java similarity index 97% rename from common/src/io/openems/common/utils/SecureRandomSingleton.java rename to io.openems.common/src/io/openems/common/utils/SecureRandomSingleton.java index 52e99c20563..59325a1ac5e 100644 --- a/common/src/io/openems/common/utils/SecureRandomSingleton.java +++ b/io.openems.common/src/io/openems/common/utils/SecureRandomSingleton.java @@ -1,36 +1,36 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.utils; - -import java.security.SecureRandom; - -public class SecureRandomSingleton { - private static SecureRandom instance; - - public static synchronized SecureRandom getInstance() { - if (SecureRandomSingleton.instance == null) { - SecureRandomSingleton.instance = new SecureRandom(); - } - return SecureRandomSingleton.instance; - } - - private SecureRandomSingleton() {} -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.utils; + +import java.security.SecureRandom; + +public class SecureRandomSingleton { + private static SecureRandom instance; + + public static synchronized SecureRandom getInstance() { + if (SecureRandomSingleton.instance == null) { + SecureRandomSingleton.instance = new SecureRandom(); + } + return SecureRandomSingleton.instance; + } + + private SecureRandomSingleton() {} +} diff --git a/common/src/io/openems/common/utils/StringUtils.java b/io.openems.common/src/io/openems/common/utils/StringUtils.java similarity index 100% rename from common/src/io/openems/common/utils/StringUtils.java rename to io.openems.common/src/io/openems/common/utils/StringUtils.java diff --git a/common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java similarity index 99% rename from common/src/io/openems/common/websocket/AbstractWebsocketServer.java rename to io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java index 07df2825e0d..b421929b02d 100644 --- a/common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java @@ -37,7 +37,6 @@ protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Opt protected abstract void _onClose(WebSocket websocket, Optional sessionOpt); - @SuppressWarnings("deprecation") public AbstractWebsocketServer(int port, M sessionManager) { super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); this.sessionManager = sessionManager; diff --git a/common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java similarity index 100% rename from common/src/io/openems/common/websocket/CurrentDataWorker.java rename to io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java diff --git a/common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java similarity index 95% rename from common/src/io/openems/common/websocket/DefaultMessages.java rename to io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 9f1f5749682..85db6caef14 100644 --- a/common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -1,357 +1,357 @@ -package io.openems.common.websocket; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map.Entry; -import java.util.Optional; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openems.common.types.ChannelAddress; -import io.openems.common.types.DeviceImpl; -import io.openems.common.types.FieldValue; -import io.openems.common.types.NumberFieldValue; -import io.openems.common.types.StringFieldValue; - -public class DefaultMessages { - - /** - *
    -	 *	{
    -	 *		authenticate: {
    -	 *			mode: "allow",
    -	 *			token: String
    -	 *		}, metadata: {
    -	 *			devices: [{
    -	 *				name: String,
    -	 *				comment: String,
    -	 *				producttype: String,
    -	 *				role: "admin" | "installer" | "owner" | "guest",
    -	 *				online: boolean
    -	 *			}]
    -	 *		}
    -	 *	}
    -	 * - authenticate.role is only sent for OpenEMS Edge
    -	 * - metadata.devices is only sent for OpenEMS Backend
    -	 * 
    - * - * @param token - * @return - */ - public static JsonObject browserConnectionSuccessfulReply(String token, Optional roleOpt, - Collection devices) { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "allow"); - if (roleOpt.isPresent()) { - jAuthenticate.addProperty("role", roleOpt.get()); - } - jAuthenticate.addProperty("token", token); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - JsonObject jMetadata = new JsonObject(); - if (!devices.isEmpty()) { - JsonArray jDevices = new JsonArray(); - for (DeviceImpl device : devices) { - JsonObject jDevice = new JsonObject(); - jDevice.addProperty("name", device.getName()); - jDevice.addProperty("comment", device.getComment()); - jDevice.addProperty("producttype", device.getProducttype()); - jDevice.addProperty("role", device.getRole().toString()); - jDevice.addProperty("online", device.isOnline()); - jDevices.add(jDevice); - } - jMetadata.add("devices", jDevices); - } - j.add("metadata", jMetadata); - return j; - } - - /** - *
    -	 *	{
    -	 *		authenticate: {
    -	 *			mode: "deny"
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @param token - * @return - */ - public static JsonObject browserConnectionFailedReply() { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "deny"); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - return j; - } - - /** - *
    -	 *	{
    -	 *		authenticate: {
    -	 *			mode: "allow"
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @param token - * @return - */ - public static JsonObject openemsConnectionSuccessfulReply() { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "allow"); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - return j; - } - - /** - *
    -	 *	{
    -	 *		authenticate: {
    -	 *			mode: "deny",
    -	 *			message: String
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @param token - * @return - */ - public static JsonObject openemsConnectionFailedReply(String message) { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "deny"); - jAuthenticate.addProperty("message", message); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - return j; - } - - /** - *
    -	 *	{
    -	 *		timedata: {
    -	 *			timestamp (Long): {
    -	 *				channel: String,
    -	 *				value: String | Number
    -	 *			}
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @param token - * @return - */ - public static JsonObject timestampedData(long timestamp, HashMap> queue) { - JsonObject jTimestamp = new JsonObject(); - for (Entry> entry : queue.entrySet()) { - String address = entry.getKey().toString(); - FieldValue fieldValue = entry.getValue(); - if (fieldValue instanceof NumberFieldValue) { - jTimestamp.addProperty(address, ((NumberFieldValue) fieldValue).value); - } else if (fieldValue instanceof StringFieldValue) { - jTimestamp.addProperty(address, ((StringFieldValue) fieldValue).value); - } - } - JsonObject jTimedata = new JsonObject(); - jTimedata.add(String.valueOf(timestamp), jTimestamp); - JsonObject j = new JsonObject(); - j.add("timedata", jTimedata); - return j; - } - - /** - *
    -	 *	{
    -	 *		config: {
    -	 *			...
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @param token - * @return - */ - public static JsonObject configQueryReply(JsonObject config) { - JsonObject j = new JsonObject(); - j.add("config", config); - return j; - } - - /** - *
    -	 *	{
    -	 *		id: [string],
    -	 *		currentData: {[{ 
    -	 *			channel: string,
    -	 *			value: any
    -	 *		}]}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject currentData(JsonArray jId, Optional deviceNameOpt, JsonObject jCurrentData) { - JsonObject j = new JsonObject(); - j.add("id", jId); - if(deviceNameOpt.isPresent()) { - j.addProperty("device", deviceNameOpt.get()); - } - j.add("currentData", jCurrentData); - return j; - } - - /** - *
    -	 *	{
    -	 *		id: [string]
    -	 *		historicData: {
    -	 *			data: [{
    -	 *				time: ...,
    -	 *				channels: {
    -	 *					thing: {
    -	 *						channel: any
    -	 *					} 
    -	 *				}
    -	 *			}]
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject historicDataQueryReply(JsonArray jId, JsonArray jData) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jHistoricData = new JsonObject(); - jHistoricData.add("data", jData); - j.add("historicData", jHistoricData); - return j; - } - - /** - *
    -	 *	{
    -	 *		notification: {
    -	 *			id: string[],
    -	 *			type: string,
    -	 *			message: string,
    -	 *			code: number,
    -	 *			params: string[]
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject notification(JsonArray jId, Notification code, String message, Object... params) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jNotification = new JsonObject(); - jNotification.addProperty("type", code.getType().toString().toLowerCase()); - jNotification.addProperty("message", message); - jNotification.addProperty("code", code.getValue()); - JsonArray jParams = new JsonArray(); - for (Object param : params) { - jParams.add(param.toString()); - } - jNotification.add("params", jParams); - j.add("notification", jNotification); - return j; - } - - /** - *
    -	 *	{
    -	 *		currentData: {
    -	 *			mode: 'subscribe',
    -	 *			channels: {}
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannels) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jCurrentData = new JsonObject(); - jCurrentData.addProperty("mode", "subscribe"); - jCurrentData.add("channels", jChannels); - j.add("currentData", jCurrentData); - return j; - } - - /** - *
    -	 *	{
    -	 *		id: [string],
    -	 *		log: {
    -	 *			times: number,
    -	 *			level: string,
    -	 *			source: string,
    -	 *			message: string
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject log(JsonArray jId, long timestamp, String level, String source, String message) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jLog = new JsonObject(); - jLog.addProperty("time", timestamp); - jLog.addProperty("level", level); - jLog.addProperty("source", source); - jLog.addProperty("message", message); - j.add("log", jLog); - return j; - } - - /** - *
    -	 *	{
    -	 *		id: [string],
    -	 *		log: {
    -	 *			mode: "unsubscribe"
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject logUnsubscribe(JsonArray jId) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jLog = new JsonObject(); - jLog.addProperty("mode", "unsubscribe"); - j.add("log", jLog); - return j; - } - - /** - *
    -	 *	{
    -	 *		id: [string],
    -	 *		system: {
    -	 *			mode: "executeReply",
    -	 *			output: string
    -	 *		}
    -	 *	}
    -	 * 
    - * - * @return - */ - public static JsonObject systemExecuteReply(JsonArray jId, String output) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jSystem = new JsonObject(); - jSystem.addProperty("mode", "executeReply"); - jSystem.addProperty("output", output); - j.add("system", jSystem); - return j; - } -} +package io.openems.common.websocket; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Optional; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.DeviceImpl; +import io.openems.common.types.FieldValue; +import io.openems.common.types.NumberFieldValue; +import io.openems.common.types.StringFieldValue; + +public class DefaultMessages { + + /** + *
    +	 *	{
    +	 *		authenticate: {
    +	 *			mode: "allow",
    +	 *			token: String
    +	 *		}, metadata: {
    +	 *			devices: [{
    +	 *				name: String,
    +	 *				comment: String,
    +	 *				producttype: String,
    +	 *				role: "admin" | "installer" | "owner" | "guest",
    +	 *				online: boolean
    +	 *			}]
    +	 *		}
    +	 *	}
    +	 * - authenticate.role is only sent for OpenEMS Edge
    +	 * - metadata.devices is only sent for OpenEMS Backend
    +	 * 
    + * + * @param token + * @return + */ + public static JsonObject browserConnectionSuccessfulReply(String token, Optional roleOpt, + Collection devices) { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "allow"); + if (roleOpt.isPresent()) { + jAuthenticate.addProperty("role", roleOpt.get()); + } + jAuthenticate.addProperty("token", token); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + JsonObject jMetadata = new JsonObject(); + if (!devices.isEmpty()) { + JsonArray jDevices = new JsonArray(); + for (DeviceImpl device : devices) { + JsonObject jDevice = new JsonObject(); + jDevice.addProperty("name", device.getName()); + jDevice.addProperty("comment", device.getComment()); + jDevice.addProperty("producttype", device.getProducttype()); + jDevice.addProperty("role", device.getRole().toString()); + jDevice.addProperty("online", device.isOnline()); + jDevices.add(jDevice); + } + jMetadata.add("devices", jDevices); + } + j.add("metadata", jMetadata); + return j; + } + + /** + *
    +	 *	{
    +	 *		authenticate: {
    +	 *			mode: "deny"
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @param token + * @return + */ + public static JsonObject browserConnectionFailedReply() { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "deny"); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + return j; + } + + /** + *
    +	 *	{
    +	 *		authenticate: {
    +	 *			mode: "allow"
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @param token + * @return + */ + public static JsonObject openemsConnectionSuccessfulReply() { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "allow"); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + return j; + } + + /** + *
    +	 *	{
    +	 *		authenticate: {
    +	 *			mode: "deny",
    +	 *			message: String
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @param token + * @return + */ + public static JsonObject openemsConnectionFailedReply(String message) { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "deny"); + jAuthenticate.addProperty("message", message); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + return j; + } + + /** + *
    +	 *	{
    +	 *		timedata: {
    +	 *			timestamp (Long): {
    +	 *				channel: String,
    +	 *				value: String | Number
    +	 *			}
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @param token + * @return + */ + public static JsonObject timestampedData(long timestamp, HashMap> queue) { + JsonObject jTimestamp = new JsonObject(); + for (Entry> entry : queue.entrySet()) { + String address = entry.getKey().toString(); + FieldValue fieldValue = entry.getValue(); + if (fieldValue instanceof NumberFieldValue) { + jTimestamp.addProperty(address, ((NumberFieldValue) fieldValue).value); + } else if (fieldValue instanceof StringFieldValue) { + jTimestamp.addProperty(address, ((StringFieldValue) fieldValue).value); + } + } + JsonObject jTimedata = new JsonObject(); + jTimedata.add(String.valueOf(timestamp), jTimestamp); + JsonObject j = new JsonObject(); + j.add("timedata", jTimedata); + return j; + } + + /** + *
    +	 *	{
    +	 *		config: {
    +	 *			...
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @param token + * @return + */ + public static JsonObject configQueryReply(JsonObject config) { + JsonObject j = new JsonObject(); + j.add("config", config); + return j; + } + + /** + *
    +	 *	{
    +	 *		id: [string],
    +	 *		currentData: {[{ 
    +	 *			channel: string,
    +	 *			value: any
    +	 *		}]}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject currentData(JsonArray jId, Optional deviceNameOpt, JsonObject jCurrentData) { + JsonObject j = new JsonObject(); + j.add("id", jId); + if(deviceNameOpt.isPresent()) { + j.addProperty("device", deviceNameOpt.get()); + } + j.add("currentData", jCurrentData); + return j; + } + + /** + *
    +	 *	{
    +	 *		id: [string]
    +	 *		historicData: {
    +	 *			data: [{
    +	 *				time: ...,
    +	 *				channels: {
    +	 *					thing: {
    +	 *						channel: any
    +	 *					} 
    +	 *				}
    +	 *			}]
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject historicDataQueryReply(JsonArray jId, JsonArray jData) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jHistoricData = new JsonObject(); + jHistoricData.add("data", jData); + j.add("historicData", jHistoricData); + return j; + } + + /** + *
    +	 *	{
    +	 *		notification: {
    +	 *			id: string[],
    +	 *			type: string,
    +	 *			message: string,
    +	 *			code: number,
    +	 *			params: string[]
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject notification(JsonArray jId, Notification code, String message, Object... params) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jNotification = new JsonObject(); + jNotification.addProperty("type", code.getType().toString().toLowerCase()); + jNotification.addProperty("message", message); + jNotification.addProperty("code", code.getValue()); + JsonArray jParams = new JsonArray(); + for (Object param : params) { + jParams.add(param.toString()); + } + jNotification.add("params", jParams); + j.add("notification", jNotification); + return j; + } + + /** + *
    +	 *	{
    +	 *		currentData: {
    +	 *			mode: 'subscribe',
    +	 *			channels: {}
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannels) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jCurrentData = new JsonObject(); + jCurrentData.addProperty("mode", "subscribe"); + jCurrentData.add("channels", jChannels); + j.add("currentData", jCurrentData); + return j; + } + + /** + *
    +	 *	{
    +	 *		id: [string],
    +	 *		log: {
    +	 *			times: number,
    +	 *			level: string,
    +	 *			source: string,
    +	 *			message: string
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject log(JsonArray jId, long timestamp, String level, String source, String message) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jLog = new JsonObject(); + jLog.addProperty("time", timestamp); + jLog.addProperty("level", level); + jLog.addProperty("source", source); + jLog.addProperty("message", message); + j.add("log", jLog); + return j; + } + + /** + *
    +	 *	{
    +	 *		id: [string],
    +	 *		log: {
    +	 *			mode: "unsubscribe"
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject logUnsubscribe(JsonArray jId) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jLog = new JsonObject(); + jLog.addProperty("mode", "unsubscribe"); + j.add("log", jLog); + return j; + } + + /** + *
    +	 *	{
    +	 *		id: [string],
    +	 *		system: {
    +	 *			mode: "executeReply",
    +	 *			output: string
    +	 *		}
    +	 *	}
    +	 * 
    + * + * @return + */ + public static JsonObject systemExecuteReply(JsonArray jId, String output) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jSystem = new JsonObject(); + jSystem.addProperty("mode", "executeReply"); + jSystem.addProperty("output", output); + j.add("system", jSystem); + return j; + } +} diff --git a/common/src/io/openems/common/websocket/LogBehaviour.java b/io.openems.common/src/io/openems/common/websocket/LogBehaviour.java similarity index 100% rename from common/src/io/openems/common/websocket/LogBehaviour.java rename to io.openems.common/src/io/openems/common/websocket/LogBehaviour.java diff --git a/common/src/io/openems/common/websocket/Notification.java b/io.openems.common/src/io/openems/common/websocket/Notification.java similarity index 100% rename from common/src/io/openems/common/websocket/Notification.java rename to io.openems.common/src/io/openems/common/websocket/Notification.java diff --git a/common/src/io/openems/common/websocket/NotificationType.java b/io.openems.common/src/io/openems/common/websocket/NotificationType.java similarity index 97% rename from common/src/io/openems/common/websocket/NotificationType.java rename to io.openems.common/src/io/openems/common/websocket/NotificationType.java index d9b7fc098a2..4d087b1e5b3 100644 --- a/common/src/io/openems/common/websocket/NotificationType.java +++ b/io.openems.common/src/io/openems/common/websocket/NotificationType.java @@ -1,25 +1,25 @@ -/******************************************************************************* - * 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.common.websocket; - -public enum NotificationType { - SUCCESS, ERROR, WARNING, INFO, LOG; -} +/******************************************************************************* + * 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.common.websocket; + +public enum NotificationType { + SUCCESS, ERROR, WARNING, INFO, LOG; +} diff --git a/common/src/io/openems/common/websocket/WebSocketUtils.java b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java similarity index 97% rename from common/src/io/openems/common/websocket/WebSocketUtils.java rename to io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java index 0c47c738b96..7072b091f95 100644 --- a/common/src/io/openems/common/websocket/WebSocketUtils.java +++ b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java @@ -1,113 +1,113 @@ -package io.openems.common.websocket; - -import java.time.Period; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.java_websocket.exceptions.WebsocketNotConnectedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openems.common.api.TimedataSource; -import io.openems.common.session.Role; -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.StringUtils; - -public class WebSocketUtils { - - private static Logger log = LoggerFactory.getLogger(WebSocketUtils.class); - - public static boolean send(Optional websocketOpt, JsonObject j) { - if (!websocketOpt.isPresent()) { - log.error("Websocket is not available. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); - return false; - } else { - return WebSocketUtils.send(websocketOpt.get(), j); - } - } - - public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour, Notification code, - Object... params) { - if (!websocketOpt.isPresent()) { - log.error("Websocket is not available. Unable to send Notification [" - + String.format(code.getMessage(), params) + "]"); - return false; - } else { - return WebSocketUtils.sendNotification(websocketOpt.get(), jId, logBehaviour, code, params); - } - } - - public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour, Notification notification, Object... params) { - if (logBehaviour.equals(LogBehaviour.WRITE_TO_LOG)) { - // log message - notification.writeToLog(log, params); - } - String message = String.format(notification.getMessage(), params); - JsonObject j = DefaultMessages.notification(jId, notification, message, params); - return WebSocketUtils.send(websocket, j); - } - - /** - * Send a message to a websocket - * - * @param j - * @return true if successful, otherwise false - */ - public static boolean send(WebSocket websocket, JsonObject j) { - // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); - try { - websocket.send(j.toString()); - return true; - } catch (WebsocketNotConnectedException e) { - log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); - return false; - } - } - - /** - * Query history command - * - * @param j - */ - public static JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, - TimedataSource timedataSource, Role role) { - try { - String mode = JsonUtils.getAsString(jHistoricData, "mode"); - if (mode.equals("query")) { - /* - * Query historic data - */ - int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); - ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); - ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); - ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); - JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); - // TODO check if role is allowed to read these channels - // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); - int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); - // TODO: better calculation of sensible resolution - int resolution = 10 * 60; // 10 Minutes - if (days > 25) { - resolution = 24 * 60 * 60; // 1 Day - } else if (days > 6) { - resolution = 3 * 60 * 60; // 3 Hours - } else if (days > 2) { - resolution = 60 * 60; // 60 Minutes - } - JsonArray jData = timedataSource.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); - // send reply - return DefaultMessages.historicDataQueryReply(jMessageId, jData); - } - } catch (Exception e) { - log.error("HistoricData Error: ", e); - e.printStackTrace(); - } - return new JsonObject(); - } -} +package io.openems.common.websocket; + +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.exceptions.WebsocketNotConnectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.openems.common.api.TimedataSource; +import io.openems.common.session.Role; +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; + +public class WebSocketUtils { + + private static Logger log = LoggerFactory.getLogger(WebSocketUtils.class); + + public static boolean send(Optional websocketOpt, JsonObject j) { + if (!websocketOpt.isPresent()) { + log.error("Websocket is not available. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; + } else { + return WebSocketUtils.send(websocketOpt.get(), j); + } + } + + public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour, Notification code, + Object... params) { + if (!websocketOpt.isPresent()) { + log.error("Websocket is not available. Unable to send Notification [" + + String.format(code.getMessage(), params) + "]"); + return false; + } else { + return WebSocketUtils.sendNotification(websocketOpt.get(), jId, logBehaviour, code, params); + } + } + + public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour, Notification notification, Object... params) { + if (logBehaviour.equals(LogBehaviour.WRITE_TO_LOG)) { + // log message + notification.writeToLog(log, params); + } + String message = String.format(notification.getMessage(), params); + JsonObject j = DefaultMessages.notification(jId, notification, message, params); + return WebSocketUtils.send(websocket, j); + } + + /** + * Send a message to a websocket + * + * @param j + * @return true if successful, otherwise false + */ + public static boolean send(WebSocket websocket, JsonObject j) { + // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); + try { + websocket.send(j.toString()); + return true; + } catch (WebsocketNotConnectedException e) { + log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; + } + } + + /** + * Query history command + * + * @param j + */ + public static JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, + TimedataSource timedataSource, Role role) { + try { + String mode = JsonUtils.getAsString(jHistoricData, "mode"); + if (mode.equals("query")) { + /* + * Query historic data + */ + int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); + ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); + ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); + ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); + JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); + // TODO check if role is allowed to read these channels + // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); + int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); + // TODO: better calculation of sensible resolution + int resolution = 10 * 60; // 10 Minutes + if (days > 25) { + resolution = 24 * 60 * 60; // 1 Day + } else if (days > 6) { + resolution = 3 * 60 * 60; // 3 Hours + } else if (days > 2) { + resolution = 60 * 60; // 60 Minutes + } + JsonArray jData = timedataSource.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); + // send reply + return DefaultMessages.historicDataQueryReply(jMessageId, jData); + } + } catch (Exception e) { + log.error("HistoricData Error: ", e); + e.printStackTrace(); + } + return new JsonObject(); + } +} diff --git a/io.openems.common/src/io/openems/common/websocket/package-info.java b/io.openems.common/src/io/openems/common/websocket/package-info.java new file mode 100644 index 00000000000..25629daed80 --- /dev/null +++ b/io.openems.common/src/io/openems/common/websocket/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common.websocket; From 59df1e9ab1a45dd2fc6a592c39d0a0d65523685f Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 30 Jan 2018 22:22:41 +0100 Subject: [PATCH 017/156] Create wrapper bundle for Java-WebSocket --- io.openems.wrapper/.classpath | 7 ++++++ io.openems.wrapper/.gitignore | 2 ++ io.openems.wrapper/.project | 23 ++++++++++++++++++ .../org.eclipse.core.resources.prefs | 2 ++ .../.settings/org.eclipse.jdt.core.prefs | 11 +++++++++ io.openems.wrapper/bnd.bnd | 1 + .../jar/Java-WebSocket-1.3.6.jar | Bin 0 -> 108198 bytes io.openems.wrapper/websocket.bnd | 19 +++++++++++++++ 8 files changed, 65 insertions(+) create mode 100644 io.openems.wrapper/.classpath create mode 100644 io.openems.wrapper/.gitignore create mode 100644 io.openems.wrapper/.project create mode 100644 io.openems.wrapper/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.wrapper/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.wrapper/bnd.bnd create mode 100644 io.openems.wrapper/jar/Java-WebSocket-1.3.6.jar create mode 100644 io.openems.wrapper/websocket.bnd diff --git a/io.openems.wrapper/.classpath b/io.openems.wrapper/.classpath new file mode 100644 index 00000000000..6483fc14f18 --- /dev/null +++ b/io.openems.wrapper/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/io.openems.wrapper/.gitignore b/io.openems.wrapper/.gitignore new file mode 100644 index 00000000000..57b341172a1 --- /dev/null +++ b/io.openems.wrapper/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/generated/ diff --git a/io.openems.wrapper/.project b/io.openems.wrapper/.project new file mode 100644 index 00000000000..8ed1b92ae27 --- /dev/null +++ b/io.openems.wrapper/.project @@ -0,0 +1,23 @@ + + + io.openems.wrapper + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.wrapper/.settings/org.eclipse.core.resources.prefs b/io.openems.wrapper/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..1554c6f848a --- /dev/null +++ b/io.openems.wrapper/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/io.openems.wrapper/.settings/org.eclipse.jdt.core.prefs b/io.openems.wrapper/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.wrapper/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.wrapper/bnd.bnd b/io.openems.wrapper/bnd.bnd new file mode 100644 index 00000000000..bbeff87cc58 --- /dev/null +++ b/io.openems.wrapper/bnd.bnd @@ -0,0 +1 @@ +-sub: *.bnd \ No newline at end of file diff --git a/io.openems.wrapper/jar/Java-WebSocket-1.3.6.jar b/io.openems.wrapper/jar/Java-WebSocket-1.3.6.jar new file mode 100644 index 0000000000000000000000000000000000000000..072633aed0d2e5dd963636ee1343f68dda260409 GIT binary patch literal 108198 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 M3NG{e<4BPI4`|2^ Date: Tue, 30 Jan 2018 22:22:57 +0100 Subject: [PATCH 018/156] Create temporary Logger class --- .../src/io/openems/common/utils/Log.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 io.openems.common/src/io/openems/common/utils/Log.java diff --git a/io.openems.common/src/io/openems/common/utils/Log.java b/io.openems.common/src/io/openems/common/utils/Log.java new file mode 100644 index 00000000000..e5dd0c04356 --- /dev/null +++ b/io.openems.common/src/io/openems/common/utils/Log.java @@ -0,0 +1,20 @@ +package io.openems.common.utils; + +public class Log { + public static final void info(String message) { + System.out.println("INFO " + message); + } + + public static final void warn(String message) { + System.out.println("WARN " + message); + } + + public static final void error(String message) { + System.out.println("ERROR " + message); + } + + public static final void error(String message, Exception e) { + System.out.println("ERROR " + message); + e.printStackTrace(); + } +} From 61a2dda59a9a98316ce7be12d6dbcfc31ab7dd0c Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 30 Jan 2018 22:24:30 +0100 Subject: [PATCH 019/156] Working version, including configuration admin --- .../BrowserWebsocketSingleton.java | 48 -- .../openemswebsocket/OpenemsWebsocket.java | 32 - cnf/central.xml | 5 - .../org.eclipse.core.resources.prefs | 2 +- .../io.openems.backend.application.bndrun | 21 +- ...viderImplTest.java => BackendAppTest.java} | 18 +- .../bnd.bnd | 8 +- .../api/BrowserWebsocketService.java | 13 + .../impl/BrowserWebsocketImpl.java | 31 - .../.classpath | 0 .../.gitignore | 0 .../.project | 23 + .../org.eclipse.core.resources.prefs | 4 +- .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 14 +- .../debug.bndrun | 0 ...s.backend.browserwebsocket.provider.bndrun | 4 +- .../readme.md | 0 .../provider/BrowserWebsocketProvider.java | 59 ++ .../internal/BackendCurrentDataWorker.java | 2 +- .../provider}/internal/BrowserSession.java | 2 +- .../internal/BrowserSessionData.java | 2 +- .../internal/BrowserSessionManager.java | 2 +- .../provider}/internal/BrowserWebsocket.java | 129 +++- .../provider/package-info.java | 2 + .../impl/ProviderImplTest.java | 4 +- .../metadata/api}/MetadataDeviceModel.java | 2 +- .../backend/metadata/api/MetadataService.java | 3 +- .../org.eclipse.core.resources.prefs | 2 +- .../odoo/{OdooImpl.java => OdooProvider.java} | 34 +- .../backend/metadata/odoo}/package-info.java | 2 +- .../metadata/odoo/ProviderImplTest.java | 4 +- .../.classpath | 8 + .../.gitignore | 3 + .../.project | 2 +- .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 11 + .../bnd.bnd | 20 + .../readme.md | 8 + .../api/OpenemsWebsocketService.java | 19 + .../openemswebsocket/api/package-info.java | 2 + .../test/.gitignore | 0 .../.classpath | 8 + .../.gitignore | 3 + .../.project | 23 + .../org.eclipse.core.resources.prefs | 7 + .../.settings/org.eclipse.jdt.core.prefs | 11 + .../bnd.bnd | 28 + .../debug.bndrun | 13 + ...enems.backend.openemswebsocket.impl.bndrun | 14 + .../readme.md | 8 + .../provider/OpenemsWebsocketProvider.java | 93 +++ .../provider/internal}/OpenemsSession.java | 2 +- .../internal}/OpenemsSessionData.java | 4 +- .../internal}/OpenemsSessionManager.java | 2 +- .../provider/internal/OpenemsWebsocket.java | 678 +++++++++--------- .../provider/package-info.java | 2 + .../impl/ProviderImplTest.java | 26 + .../backend/timedata/api/TimedataService.java | 28 + .../org.eclipse.core.resources.prefs | 2 +- io.openems.backend.timedata.influx/bnd.bnd | 3 +- .../InfluxImpl.java => InfluxProvider.java} | 41 +- .../influx/internal}/InfluxdbUtils.java | 623 ++++++++-------- .../timedata/influx/ProviderImplTest.java | 4 +- io.openems.common/bnd.bnd | 4 +- .../io/openems/common/api/TimedataSource.java | 36 - .../common/websocket/WebSocketUtils.java | 57 +- 67 files changed, 1301 insertions(+), 970 deletions(-) delete mode 100644 backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java delete mode 100644 backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocket.java rename io.openems.backend.application/test/io/openems/backend/application/{ProviderImplTest.java => BackendAppTest.java} (55%) delete mode 100644 io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/.classpath (100%) rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/.gitignore (100%) create mode 100644 io.openems.backend.browserwebsocket.provider/.project rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/.settings/org.eclipse.core.resources.prefs (53%) rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/bnd.bnd (54%) rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/debug.bndrun (100%) rename io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun => io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun (75%) rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/readme.md (100%) create mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java rename {io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl => io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider}/internal/BackendCurrentDataWorker.java (95%) rename {io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl => io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider}/internal/BrowserSession.java (85%) rename {io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl => io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider}/internal/BrowserSessionData.java (96%) rename {io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl => io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider}/internal/BrowserSessionManager.java (82%) rename {io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl => io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider}/internal/BrowserWebsocket.java (73%) create mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.browserwebsocket.provider}/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java (66%) rename {backend/src/main/java/io/openems/backend/metadata/api/device => io.openems.backend.metadata.api/src/io/openems/backend/metadata/api}/MetadataDeviceModel.java (86%) rename io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/{OdooImpl.java => OdooProvider.java} (87%) rename {io.openems.common/src/io/openems/common/api => io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo}/package-info.java (53%) create mode 100644 io.openems.backend.openemswebsocket.api/.classpath create mode 100644 io.openems.backend.openemswebsocket.api/.gitignore rename {io.openems.backend.browserwebsocket.impl => io.openems.backend.openemswebsocket.api}/.project (89%) create mode 100644 io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.openemswebsocket.api/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.openemswebsocket.api/bnd.bnd create mode 100644 io.openems.backend.openemswebsocket.api/readme.md create mode 100644 io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java create mode 100644 io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java create mode 100644 io.openems.backend.openemswebsocket.api/test/.gitignore create mode 100644 io.openems.backend.openemswebsocket.provider/.classpath create mode 100644 io.openems.backend.openemswebsocket.provider/.gitignore create mode 100644 io.openems.backend.openemswebsocket.provider/.project create mode 100644 io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.openemswebsocket.provider/bnd.bnd create mode 100644 io.openems.backend.openemswebsocket.provider/debug.bndrun create mode 100644 io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun create mode 100644 io.openems.backend.openemswebsocket.provider/readme.md create mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java rename {backend/src/main/java/io/openems/backend/openemswebsocket/session => io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal}/OpenemsSession.java (83%) rename {backend/src/main/java/io/openems/backend/openemswebsocket/session => io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal}/OpenemsSessionData.java (88%) rename {backend/src/main/java/io/openems/backend/openemswebsocket/session => io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal}/OpenemsSessionManager.java (95%) rename backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocketSingleton.java => io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java (87%) create mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java create mode 100644 io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java rename io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/{api/InfluxImpl.java => InfluxProvider.java} (91%) rename {common/src/io/openems/common/utils => io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal}/InfluxdbUtils.java (96%) delete mode 100644 io.openems.common/src/io/openems/common/api/TimedataSource.java diff --git a/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java b/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java deleted file mode 100644 index d80987090c3..00000000000 --- a/backend/src/main/java/io/openems/backend/browserwebsocket/BrowserWebsocketSingleton.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.openems.backend.browserwebsocket; - -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import javax.management.Notification; - -import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.handshake.ClientHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.backend.browserwebsocket.session.BackendCurrentDataWorker; -import io.openems.backend.browserwebsocket.session.BrowserSession; -import io.openems.backend.browserwebsocket.session.BrowserSessionData; -import io.openems.backend.browserwebsocket.session.BrowserSessionManager; -import io.openems.backend.metadata.Metadata; -import io.openems.backend.openemswebsocket.OpenemsWebsocket; -import io.openems.backend.openemswebsocket.OpenemsWebsocketSingleton; -import io.openems.backend.openemswebsocket.session.OpenemsSession; -import io.openems.backend.timedata.Timedata; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.Device; -import io.openems.common.types.DeviceImpl; -import io.openems.common.utils.JsonUtils; -import io.openems.common.websocket.AbstractWebsocketServer; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.LogBehaviour; -import io.openems.common.websocket.WebSocketUtils; - -/** - * Handles connections from a browser. - * - * @author stefan.feilmeier - * - */ -public class BrowserWebsocketSingleton - extends AbstractWebsocketServer { - private final Logger log = LoggerFactory.getLogger(BrowserWebsocketSingleton.class); - - diff --git a/backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocket.java b/backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocket.java deleted file mode 100644 index 17ae2bd983b..00000000000 --- a/backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocket.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.openems.backend.openemswebsocket; - -/** - * Provider for OpenemsWebsocketServer singleton - * - * @author stefan.feilmeier - * - */ -public class OpenemsWebsocket { - - private static OpenemsWebsocketSingleton instance; - - /** - * Initialize and start the Websocketserver - * - * @param port - * @throws Exception - */ - public static synchronized void initialize(int port) { - OpenemsWebsocket.instance = new OpenemsWebsocketSingleton(port); - OpenemsWebsocket.instance.start(); - } - - /** - * Returns the singleton instance - * - * @return - */ - public static synchronized OpenemsWebsocketSingleton instance() { - return OpenemsWebsocket.instance; - } -} \ No newline at end of file diff --git a/cnf/central.xml b/cnf/central.xml index 144326d1ecc..fb92f0ac46a 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -61,11 +61,6 @@ org.apache.servicemix.bundles.ws-commons-util 1.0.2_2 - - org.java-websocket - Java-WebSocket - 1.3.6 - diff --git a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs index 2737fd0622a..24ef32c8232 100644 --- a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,6 @@ eclipse.preferences.version=1 encoding//src/io/openems/backend/application/BackendApp.java=UTF-8 -encoding//test/io/openems/backend/application/ProviderImplTest.java=UTF-8 +encoding//test/io/openems/backend/application/BackendAppTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/io.openems.backend.application.bndrun=UTF-8 encoding/readme.md=UTF-8 diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 3951c18cf55..5b73302ec36 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -5,8 +5,7 @@ Bundle-Version: 1.0.0.${tstamp} Bundle-SymbolicName: io.openems.backend.application.launch -JPM-Command: provider - +JPM-Command: openems-backend -runrequires: \ osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ @@ -23,7 +22,12 @@ JPM-Command: provider osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.influxdb-java)',\ osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.okhttp)',\ osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.okio)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.retrofit)' + osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.retrofit)',\ + osgi.identity;filter:='(osgi.identity=io.openems.wrapper.websocket)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.configadmin)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.provider)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' + -resolve: auto @@ -31,11 +35,16 @@ JPM-Command: provider com.google.gson;version='[2.8.2,2.8.3)',\ com.google.guava;version='[19.0.0,19.0.1)',\ io.openems.backend.application;version=snapshot,\ + io.openems.backend.browserwebsocket.api;version=snapshot,\ + io.openems.backend.browserwebsocket.provider;version=snapshot,\ io.openems.backend.common;version=snapshot,\ io.openems.backend.metadata.odoo;version=snapshot,\ + io.openems.backend.openemswebsocket.api;version=snapshot,\ + io.openems.backend.openemswebsocket.provider;version=snapshot,\ io.openems.backend.timedata.api;version=snapshot,\ io.openems.backend.timedata.influx;version=snapshot,\ io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ json;version='[20160212.0.0,20160212.0.1)',\ org.apache.commons.codec;version='[1.10.0,1.10.1)',\ org.apache.commons.fileupload;version='[1.3.2,1.3.3)',\ @@ -61,4 +70,8 @@ JPM-Command: provider osgi.enroute.executor.simple.provider;version='[2.1.0,2.1.1)',\ osgi.enroute.logger.simple.provider;version='[2.1.0,2.1.1)',\ osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)' \ No newline at end of file + osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)' +-runproperties: felix.cm.dir=C:/openems-config +#-runproperties: felix.cm.dir=/etc/openems.d +-runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' +-runee: JavaSE-1.8 \ No newline at end of file diff --git a/io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java b/io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java similarity index 55% rename from io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java rename to io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java index b788316a58b..e19ef5d0f73 100644 --- a/io.openems.backend.application/test/io/openems/backend/application/ProviderImplTest.java +++ b/io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java @@ -11,16 +11,12 @@ * */ -public class ProviderImplTest { - - /* - * Example test method - */ - - @Test - public void simple() { - BackendApp impl = new BackendApp(); - assertNotNull(impl); - } +public class BackendAppTest { + +// @Test +// public void simple() { +// BackendApp impl = new BackendApp(); +// assertNotNull(impl); +// } } diff --git a/io.openems.backend.browserwebsocket.api/bnd.bnd b/io.openems.backend.browserwebsocket.api/bnd.bnd index 89805b5f67f..f12cf5d3f22 100644 --- a/io.openems.backend.browserwebsocket.api/bnd.bnd +++ b/io.openems.backend.browserwebsocket.api/bnd.bnd @@ -7,13 +7,11 @@ Bundle-Version: 1.0.0.${tstamp} Export-Package: \ io.openems.backend.browserwebsocket.api -Require-Capability: \ - compile-only - -includeresource: {readme.md} --buildpath: \ - osgi.enroute.base.api;version=2.1 +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.wrapper.websocket;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java index a7bf79d079c..a3859524110 100644 --- a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java +++ b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java @@ -1,8 +1,21 @@ package io.openems.backend.browserwebsocket.api; +import java.util.Optional; +import java.util.Set; + +import org.java_websocket.WebSocket; import org.osgi.annotation.versioning.ProviderType; @ProviderType public interface BrowserWebsocketService { + /** + * Announce browserWebsocket that this OpenEMS Edge was connected + * + * @param deviceNames + */ + void openemsConnectionOpened(Set deviceNames); + + Optional getWebsocketByToken(String token); + } diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java b/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java deleted file mode 100644 index 8f9be439a6f..00000000000 --- a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.openems.backend.browserwebsocket.impl; - -import java.util.Map; - -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; -import org.osgi.service.metatype.annotations.Designate; - -@Designate(ocd = BrowserWebsocketImpl.Config.class, factory = false) -@Component(name = "io.openems.backend.browserwebsocket.impl") -public class BrowserWebsocketImpl { - - @ObjectClassDefinition - @interface Config { - int port(); - } - - private int port; - - @Activate - void activate(Config config) { - this.port = config.port(); - } - - @Deactivate - void deactivate() { - } - -} diff --git a/io.openems.backend.browserwebsocket.impl/.classpath b/io.openems.backend.browserwebsocket.provider/.classpath similarity index 100% rename from io.openems.backend.browserwebsocket.impl/.classpath rename to io.openems.backend.browserwebsocket.provider/.classpath diff --git a/io.openems.backend.browserwebsocket.impl/.gitignore b/io.openems.backend.browserwebsocket.provider/.gitignore similarity index 100% rename from io.openems.backend.browserwebsocket.impl/.gitignore rename to io.openems.backend.browserwebsocket.provider/.gitignore diff --git a/io.openems.backend.browserwebsocket.provider/.project b/io.openems.backend.browserwebsocket.provider/.project new file mode 100644 index 00000000000..ee83ee8bf77 --- /dev/null +++ b/io.openems.backend.browserwebsocket.provider/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.browserwebsocket.provider + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs similarity index 53% rename from io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs rename to io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs index 2356310f168..c00dc701bd2 100644 --- a/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs @@ -1,7 +1,7 @@ eclipse.preferences.version=1 -encoding//src/io/openems/backend/browserwebsocket/impl/BrowserWebsocketImpl.java=UTF-8 +encoding//src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java=UTF-8 encoding//test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/debug.bndrun=UTF-8 -encoding/io.openems.backend.browserwebsocket.impl.bndrun=UTF-8 +encoding/io.openems.backend.browserwebsocket.provider.bndrun=UTF-8 encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.browserwebsocket.impl/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.browserwebsocket.impl/bnd.bnd b/io.openems.backend.browserwebsocket.provider/bnd.bnd similarity index 54% rename from io.openems.backend.browserwebsocket.impl/bnd.bnd rename to io.openems.backend.browserwebsocket.provider/bnd.bnd index f32ec8a9645..1ff0f8672cd 100644 --- a/io.openems.backend.browserwebsocket.impl/bnd.bnd +++ b/io.openems.backend.browserwebsocket.provider/bnd.bnd @@ -1,27 +1,25 @@ # -# io.openems.backend.browserwebsocket.impl PROVIDER BUNDLE +# io.openems.backend.browserwebsocket.provider PROVIDER BUNDLE # Bundle-Version: 1.0.0.${tstamp} -Export-Package: \ - io.openems.backend.browserwebsocket.impl.api;-provide=true +Export-Package: io.openems.backend.browserwebsocket.provider -Private-Package: \ - io.openems.backend.browserwebsocket.impl,\ - io.openems.backend.browserwebsocket.impl.internal +Private-Package: io.openems.backend.browserwebsocket.provider.internal -includeresource: {readme.md} -buildpath: \ osgi.enroute.base.api;version=2.1,\ io.openems.backend.browserwebsocket.api;version=latest,\ - org.java-websocket:Java-WebSocket,\ com.google.guava,\ com.google.gson,\ io.openems.common;version=latest,\ io.openems.backend.timedata.api;version=latest,\ - io.openems.backend.metadata.api;version=latest + io.openems.backend.metadata.api;version=latest,\ + io.openems.backend.openemswebsocket.api;version=latest,\ + io.openems.wrapper.websocket;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.browserwebsocket.impl/debug.bndrun b/io.openems.backend.browserwebsocket.provider/debug.bndrun similarity index 100% rename from io.openems.backend.browserwebsocket.impl/debug.bndrun rename to io.openems.backend.browserwebsocket.provider/debug.bndrun diff --git a/io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun b/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun similarity index 75% rename from io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun rename to io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun index 55f1e44a8c4..e96468a57b8 100644 --- a/io.openems.backend.browserwebsocket.impl/io.openems.backend.browserwebsocket.impl.bndrun +++ b/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun @@ -4,11 +4,11 @@ Bundle-Version: 1.0.0.${tstamp} -Bundle-SymbolicName: io.openems.backend.browserwebsocket.impl.launch +Bundle-SymbolicName: io.openems.backend.browserwebsocket.provider.launch JPM-Command: provider -runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.impl.provider)' + osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' -runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.browserwebsocket.impl/readme.md b/io.openems.backend.browserwebsocket.provider/readme.md similarity index 100% rename from io.openems.backend.browserwebsocket.impl/readme.md rename to io.openems.backend.browserwebsocket.provider/readme.md diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java new file mode 100644 index 00000000000..7b7a4ae842c --- /dev/null +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java @@ -0,0 +1,59 @@ +package io.openems.backend.browserwebsocket.provider; + +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.ObjectClassDefinition; + +import io.openems.backend.browserwebsocket.provider.internal.BrowserWebsocket; +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; +import io.openems.backend.timedata.api.TimedataService; +import io.openems.common.exceptions.OpenemsException; + +import org.osgi.service.metatype.annotations.Designate; + +@Designate(ocd = BrowserWebsocketProvider.Config.class, factory = false) +@Component(name = "BrowserWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE) +public class BrowserWebsocketProvider { + + @ObjectClassDefinition + @interface Config { + int port(); + } + + @Activate + void activate(Config config) { + System.out.println("Activate BrowserWebsocket"); + try { + this.browserWebsocket = new BrowserWebsocket(config.port()); + } catch (OpenemsException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Deactivate + void deactivate() { + System.out.println("Deactivate BrowserWebsocket"); + } + + private BrowserWebsocket browserWebsocket = null; + + @Reference + void setOpenemsWebsocketService(OpenemsWebsocketService openemsWebsocketService) { + this.browserWebsocket.setOpenemsWebsocketService(openemsWebsocketService); + } + + @Reference + void setMetadataService(MetadataService metadataService) { + this.browserWebsocket.setMetadataService(metadataService); + } + + @Reference + void setTimedataService(TimedataService timedataService) { + this.browserWebsocket.setTimedataService(timedataService); + } +} diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BackendCurrentDataWorker.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java similarity index 95% rename from io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BackendCurrentDataWorker.java rename to io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java index de6aaa8df8e..7ef3d1f60ab 100644 --- a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BackendCurrentDataWorker.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java @@ -1,4 +1,4 @@ -package io.openems.backend.browserwebsocket.impl.internal; +package io.openems.backend.browserwebsocket.provider.internal; import java.util.Optional; diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSession.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java similarity index 85% rename from io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSession.java rename to io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java index 143d7c3da5b..6d7d5f0b58f 100644 --- a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSession.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java @@ -1,4 +1,4 @@ -package io.openems.backend.browserwebsocket.impl.internal; +package io.openems.backend.browserwebsocket.provider.internal; import io.openems.common.session.Session; diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionData.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java similarity index 96% rename from io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionData.java rename to io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java index f1891a206da..eca97dec544 100644 --- a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionData.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java @@ -1,4 +1,4 @@ -package io.openems.backend.browserwebsocket.impl.internal; +package io.openems.backend.browserwebsocket.provider.internal; import java.util.Collection; import java.util.Optional; diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionManager.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java similarity index 82% rename from io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionManager.java rename to io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java index 208c1b53e17..e9f8f1d02e4 100644 --- a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserSessionManager.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java @@ -1,4 +1,4 @@ -package io.openems.backend.browserwebsocket.impl.internal; +package io.openems.backend.browserwebsocket.provider.internal; import io.openems.common.session.SessionManager; diff --git a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java similarity index 73% rename from io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java rename to io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java index 23f7220b4aa..292cd8ee815 100644 --- a/io.openems.backend.browserwebsocket.impl/src/io/openems/backend/browserwebsocket/impl/internal/BrowserWebsocket.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java @@ -1,25 +1,47 @@ -package io.openems.backend.browserwebsocket.impl.internal; +package io.openems.backend.browserwebsocket.provider.internal; +import java.util.Map.Entry; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Optional; +import java.util.Set; import org.java_websocket.WebSocket; +import org.java_websocket.framing.CloseFrame; import org.java_websocket.handshake.ClientHandshake; +import com.google.common.collect.HashMultimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; +import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; +import io.openems.common.types.Device; +import io.openems.common.types.DeviceImpl; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.Log; +import io.openems.common.utils.StringUtils; import io.openems.common.websocket.AbstractWebsocketServer; +import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.LogBehaviour; +import io.openems.common.websocket.Notification; +import io.openems.common.websocket.WebSocketUtils; public class BrowserWebsocket extends AbstractWebsocketServer { - private final MetadataService metadata; + private MetadataService metadataService; + private OpenemsWebsocketService openemsWebsocketService; + private TimedataService timedataService; - protected BrowserWebsocket(int port, MetadataService metadata) throws OpenemsException { + public BrowserWebsocket(int port) throws OpenemsException { super(port, new BrowserSessionManager()); - this.metadata = metadata; } /** @@ -63,13 +85,13 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { // check if the session is now valid and send reply to browser BrowserSessionData data = session.getData(); - + // check Odoo session and refresh info from Odoo if (!data.getOdooSessionId().isPresent()) { error = "Session-ID is missing."; } else { try { - this.metadata.getInfoWithSession(session.getData().getOdooSessionId().get()); + this.metadataService.getInfoWithSession(session.getData().getOdooSessionId().get()); } catch (OpenemsException e) { error = e.getMessage(); } @@ -77,9 +99,8 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { if (error.isEmpty()) { // add isOnline information - OpenemsWebsocketSingleton openemsWebsocket = OpenemsWebsocket.instance(); for (DeviceImpl device : data.getDevices()) { - device.setOnline(openemsWebsocket.isOpenemsWebsocketConnected(device.getName())); + device.setOnline(this.openemsWebsocketService.isWebsocketConnected(device.getName())); } // send connection successful to browser @@ -91,14 +112,14 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { // add websocket to local cache this.addWebsocket(websocket, session); - log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") + Log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") + "]."); } else { // send connection failed to browser JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); WebSocketUtils.send(websocket, jReply); - log.warn("User [" + data.getUserName() + "] connection failed. Session [" + Log.warn("User [" + data.getUserName() + "] connection failed. Session [" + data.getOdooSessionId().orElse("") + "] Error [" + error + "]."); websocket.closeConnection(CloseFrame.REFUSE, error); @@ -125,19 +146,19 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional sessionOpt = this.getSessionFromWebsocket(websocket); if (!sessionOpt.isPresent()) { - log.warn("No BrowserSession available."); + Log.warn("No BrowserSession available."); // throw new OpenemsException("No BrowserSession available."); } BrowserSession session = sessionOpt.get(); @@ -149,13 +170,13 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional openemsSessionOpt = OpenemsWebsocket.instance() - .getOpenemsSession(deviceName); - Optional openemsConfig = Optional.empty(); - if (openemsSessionOpt.isPresent()) { - openemsConfig = openemsSessionOpt.get().getData().getOpenemsConfigOpt(); - } + Optional openemsConfig = this.openemsWebsocketService.getConfig(deviceName); if (!openemsConfig.isPresent()) { // set configMode to empty, so that the request is forwarded to Edge configModeOpt = Optional.empty(); } else { - log.info("User [" + session.getData().getUserName() + Log.info("User [" + session.getData().getUserName() + "]: Sent OpenEMS-Config from cache for device [" + deviceName + "]."); JsonObject jReply = DefaultMessages.configQueryReply(openemsConfig.get()); if (deviceNameOpt.isPresent()) { @@ -246,17 +262,7 @@ private void forwardMessageToOpenems(BrowserSession session, WebSocket websocket jMessage.addProperty("role", role.name().toLowerCase()); // get OpenEMS websocket and forward message - Optional openemsWebsocketOpt = OpenemsWebsocket.instance().getOpenemsWebsocket(deviceName); - if (openemsWebsocketOpt.isPresent()) { - WebSocket openemsWebsocket = openemsWebsocketOpt.get(); - if (WebSocketUtils.send(openemsWebsocket, jMessage)) { - return; - } else { - throw new OpenemsException("Sending failed"); - } - } else { - throw new OpenemsException("Device is not connected."); - } + this.openemsWebsocketService.send(deviceName, jMessage); } /** @@ -301,7 +307,7 @@ private synchronized void currentData(BrowserSession session, WebSocket websocke } } } catch (OpenemsException e) { - log.warn(e.getMessage()); + Log.warn(e.getMessage()); } } @@ -379,4 +385,57 @@ public void openemsConnectionOpened(Set names) { } } } + + /** + * Query history command + * + * @param j + */ + private JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, + TimedataService timedataService, Role role) { + try { + String mode = JsonUtils.getAsString(jHistoricData, "mode"); + if (mode.equals("query")) { + /* + * Query historic data + */ + int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); + ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); + ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); + ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); + JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); + // TODO check if role is allowed to read these channels + // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); + int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); + // TODO: better calculation of sensible resolution + int resolution = 10 * 60; // 10 Minutes + if (days > 25) { + resolution = 24 * 60 * 60; // 1 Day + } else if (days > 6) { + resolution = 3 * 60 * 60; // 3 Hours + } else if (days > 2) { + resolution = 60 * 60; // 60 Minutes + } + JsonArray jData = timedataService.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); + // send reply + return DefaultMessages.historicDataQueryReply(jMessageId, jData); + } + } catch (Exception e) { + Log.error("HistoricData Error: ", e); + e.printStackTrace(); + } + return new JsonObject(); + } + + public void setMetadataService(MetadataService metadataService) { + this.metadataService = metadataService; + } + + public void setOpenemsWebsocketService(OpenemsWebsocketService openemsWebsocketService) { + this.openemsWebsocketService = openemsWebsocketService; + } + + public void setTimedataService(TimedataService timedataService) { + this.timedataService = timedataService; + } } diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java new file mode 100644 index 00000000000..5a448855f85 --- /dev/null +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.browserwebsocket.provider; diff --git a/io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java b/io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java similarity index 66% rename from io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java rename to io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java index 28aafc2d6e4..51216763abb 100644 --- a/io.openems.backend.browserwebsocket.impl/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java +++ b/io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java @@ -4,7 +4,7 @@ import org.junit.Test; -import io.openems.backend.browserwebsocket.impl.BrowserWebsocketImpl; +import io.openems.backend.browserwebsocket.provider.BrowserWebsocketProvider; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - BrowserWebsocketImpl impl = new BrowserWebsocketImpl(); + BrowserWebsocketProvider impl = new BrowserWebsocketProvider(); assertNotNull(impl); } diff --git a/backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDeviceModel.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDeviceModel.java similarity index 86% rename from backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDeviceModel.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDeviceModel.java index a0fa3a6fc20..f013dc17e21 100644 --- a/backend/src/main/java/io/openems/backend/metadata/api/device/MetadataDeviceModel.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDeviceModel.java @@ -1,4 +1,4 @@ -package io.openems.backend.metadata.api.device; +package io.openems.backend.metadata.api; import io.openems.common.exceptions.OpenemsException; diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 6db9642f3a3..69a953d2cc1 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -3,11 +3,12 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.session.Session; @ProviderType public interface MetadataService { public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsException; + public MetadataDeviceModel getDeviceModel(); + } diff --git a/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs index 662e7d0f817..613b3f9d649 100644 --- a/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,5 @@ eclipse.preferences.version=1 -encoding//src/io/openems/backend/metadata/odoo/OdooImpl.java=UTF-8 +encoding//src/io/openems/backend/metadata/odoo/OdooProvider.java=UTF-8 encoding//test/io/openems/backend/metadata/odoo/ProviderImplTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/debug.bndrun=UTF-8 diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java similarity index 87% rename from io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java rename to io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java index 637029390e5..34e99e5cf2d 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooImpl.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java @@ -2,10 +2,12 @@ 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.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.backend.metadata.api.MetadataDeviceModel; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.metadata.api.UserDevicesInfo; import io.openems.common.exceptions.OpenemsException; @@ -27,15 +29,18 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -@Designate(ocd = OdooImpl.Config.class, factory = false) -@Component(name = "io.openems.backend.metadata.odoo") -public class OdooImpl implements MetadataService { - +@Designate(ocd = OdooProvider.Config.class, factory = false) +@Component(name = "Odoo", configurationPolicy = ConfigurationPolicy.REQUIRE) +public class OdooProvider implements MetadataService { + @ObjectClassDefinition @interface Config { String database(); + int uid(); + String password(); + String url() default "https://www1.fenecon.de"; } @@ -43,9 +48,10 @@ public class OdooImpl implements MetadataService { private String database; private int uid; private String password; - + @Activate void activate(Config config) { + System.out.println("Activate Odoo"); this.url = config.url(); this.database = config.database(); this.uid = config.uid(); @@ -54,17 +60,19 @@ void activate(Config config) { @Deactivate void deactivate() { + System.out.println("Deactivate Odoo"); } - - // private MetadataDeviceModel deviceModel; - // @Override - // public MetadataDeviceModel getDeviceModel() { - // return deviceModel; - // } + private MetadataDeviceModel deviceModel; + + @Override + public MetadataDeviceModel getDeviceModel() { + return deviceModel; + } /** - * Tries to authenticate at the Odoo server using a sessionId from a cookie. Updates the Session object accordingly. + * Tries to authenticate at the Odoo server using a sessionId from a cookie. + * Updates the Session object accordingly. * * @param sessionId * @return @@ -103,7 +111,7 @@ public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsExcept if (j.has("result")) { UserDevicesInfo info = new UserDevicesInfo(); - + // parse the result JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); diff --git a/io.openems.common/src/io/openems/common/api/package-info.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/package-info.java similarity index 53% rename from io.openems.common/src/io/openems/common/api/package-info.java rename to io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/package-info.java index 39207229641..52f2df10d7a 100644 --- a/io.openems.common/src/io/openems/common/api/package-info.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/package-info.java @@ -1,2 +1,2 @@ @org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.common.api; +package io.openems.backend.metadata.odoo; diff --git a/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java b/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java index f10ee38dcdf..7553f3e7491 100644 --- a/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java +++ b/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java @@ -4,7 +4,7 @@ import org.junit.Test; -import io.openems.backend.metadata.odoo.OdooImpl; +import io.openems.backend.metadata.odoo.OdooProvider; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - OdooImpl impl = new OdooImpl(); + OdooProvider impl = new OdooProvider(); assertNotNull(impl); } diff --git a/io.openems.backend.openemswebsocket.api/.classpath b/io.openems.backend.openemswebsocket.api/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.openemswebsocket.api/.gitignore b/io.openems.backend.openemswebsocket.api/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.browserwebsocket.impl/.project b/io.openems.backend.openemswebsocket.api/.project similarity index 89% rename from io.openems.backend.browserwebsocket.impl/.project rename to io.openems.backend.openemswebsocket.api/.project index a3cdb5d86fd..2a905468f0f 100644 --- a/io.openems.backend.browserwebsocket.impl/.project +++ b/io.openems.backend.openemswebsocket.api/.project @@ -1,6 +1,6 @@ - io.openems.backend.browserwebsocket.impl + io.openems.backend.openemswebsocket.api diff --git a/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..e3e3c8999fe --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java=UTF-8 +encoding//src/io/openems/backend/openemswebsocket/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.openemswebsocket.api/bnd.bnd b/io.openems.backend.openemswebsocket.api/bnd.bnd new file mode 100644 index 00000000000..90e51ddd616 --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/bnd.bnd @@ -0,0 +1,20 @@ +# +# io.openems.backend.openemswebsocket.api DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: \ + io.openems.backend.openemswebsocket.api + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + com.google.gson,\ + io.openems.common;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.openemswebsocket.api/readme.md b/io.openems.backend.openemswebsocket.api/readme.md new file mode 100644 index 00000000000..4c5892dad83 --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.openemswebsocket.api + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java new file mode 100644 index 00000000000..2852c875d4c --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java @@ -0,0 +1,19 @@ +package io.openems.backend.openemswebsocket.api; + +import java.util.Optional; + +import org.osgi.annotation.versioning.ProviderType; + +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsException; + +@ProviderType +public interface OpenemsWebsocketService { + + boolean isWebsocketConnected(String name); + + Optional getConfig(String deviceName); + + void send(String deviceName, JsonObject j) throws OpenemsException; +} diff --git a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java new file mode 100644 index 00000000000..ddaf51aa0dc --- /dev/null +++ b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.openemswebsocket.api; diff --git a/io.openems.backend.openemswebsocket.api/test/.gitignore b/io.openems.backend.openemswebsocket.api/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.backend.openemswebsocket.provider/.classpath b/io.openems.backend.openemswebsocket.provider/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.openemswebsocket.provider/.gitignore b/io.openems.backend.openemswebsocket.provider/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.openemswebsocket.provider/.project b/io.openems.backend.openemswebsocket.provider/.project new file mode 100644 index 00000000000..aade4e07a07 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.openemswebsocket.provider + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..c1550120c06 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java=UTF-8 +encoding//test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.openemswebsocket.impl.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.openemswebsocket.provider/bnd.bnd b/io.openems.backend.openemswebsocket.provider/bnd.bnd new file mode 100644 index 00000000000..5890b9d429b --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/bnd.bnd @@ -0,0 +1,28 @@ +# +# io.openems.backend.openemswebsocket.impl PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + + +Private-Package: io.openems.backend.openemswebsocket.provider.internal + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.openemswebsocket.api;version=latest,\ + io.openems.backend.common;version=latest,\ + io.openems.common;version=latest,\ + io.openems.backend.metadata.api;version=latest,\ + com.google.gson,\ + com.google.guava,\ + io.openems.backend.browserwebsocket.api;version=latest,\ + io.openems.backend.timedata.api;version=latest,\ + io.openems.wrapper.websocket;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +Export-Package: io.openems.backend.openemswebsocket.provider \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.provider/debug.bndrun b/io.openems.backend.openemswebsocket.provider/debug.bndrun new file mode 100644 index 00000000000..d9b363fb482 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.openemswebsocket.impl DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.openemswebsocket.impl.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun b/io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun new file mode 100644 index 00000000000..2a8c294c657 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun @@ -0,0 +1,14 @@ +# +# io.openems.backend.openemswebsocket.impl LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.openemswebsocket.impl.launch +JPM-Command: provider + + +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.impl.provider)' + +-runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.openemswebsocket.provider/readme.md b/io.openems.backend.openemswebsocket.provider/readme.md new file mode 100644 index 00000000000..211a2be5b69 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.openemswebsocket.impl Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java new file mode 100644 index 00000000000..c484180272a --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java @@ -0,0 +1,93 @@ +package io.openems.backend.openemswebsocket.provider; + +import java.util.Optional; + +import org.java_websocket.WebSocket; +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.metatype.annotations.ObjectClassDefinition; + +import com.google.gson.JsonObject; + +import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; +import io.openems.backend.openemswebsocket.provider.internal.OpenemsSession; +import io.openems.backend.openemswebsocket.provider.internal.OpenemsWebsocket; +import io.openems.backend.timedata.api.TimedataService; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.websocket.WebSocketUtils; + +import org.osgi.service.metatype.annotations.Designate; + +@Designate(ocd = OpenemsWebsocketProvider.Config.class, factory = false) +@Component(name = "OpenemsWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE) +public class OpenemsWebsocketProvider implements OpenemsWebsocketService { + + @ObjectClassDefinition + @interface Config { + int port(); + } + + @Activate + void activate(Config config) { + System.out.println("Activate OpenemsWebsocket"); + this.openemsWebsocket = new OpenemsWebsocket(config.port()); + } + + @Deactivate + void deactivate() { + System.out.println("Deactivate OpenemsWebsocket"); + } + + private OpenemsWebsocket openemsWebsocket = null; + + @Reference + void setMetadataService(MetadataService metadataService) { + this.openemsWebsocket.setMetadataService(metadataService); + } + + @Reference(cardinality = ReferenceCardinality.OPTIONAL) // avoids recursive dependency + void setBrowserWebsocketService(BrowserWebsocketService browserWebsocketService) { + this.openemsWebsocket.setBrowserWebsocketService(browserWebsocketService); + } + + @Reference + void setTimedataService(TimedataService timedataService) { + this.openemsWebsocket.setTimedataService(timedataService); + } + + @Override + public boolean isWebsocketConnected(String name) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Optional getConfig(String deviceName) { + Optional openemsSessionOpt = this.openemsWebsocket.getOpenemsSession(deviceName); + if (openemsSessionOpt.isPresent()) { + return openemsSessionOpt.get().getData().getOpenemsConfigOpt(); + } + return Optional.empty(); + } + + @Override + public void send(String deviceName, JsonObject j) throws OpenemsException { + Optional openemsWebsocketOpt = this.openemsWebsocket.getOpenemsWebsocket(deviceName); + if (openemsWebsocketOpt.isPresent()) { + WebSocket openemsWebsocket = openemsWebsocketOpt.get(); + if (WebSocketUtils.send(openemsWebsocket, j)) { + return; + } else { + throw new OpenemsException("Sending failed"); + } + } else { + throw new OpenemsException("Device is not connected."); + } + } +} diff --git a/backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSession.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java similarity index 83% rename from backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSession.java rename to io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java index 384c5975599..aa36927f646 100644 --- a/backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSession.java +++ b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java @@ -1,4 +1,4 @@ -package io.openems.backend.openemswebsocket.session; +package io.openems.backend.openemswebsocket.provider.internal; import io.openems.common.session.Session; diff --git a/backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSessionData.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java similarity index 88% rename from backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSessionData.java rename to io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java index b068c7d0e4b..3ad5a85dc18 100644 --- a/backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSessionData.java +++ b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java @@ -1,4 +1,4 @@ -package io.openems.backend.openemswebsocket.session; +package io.openems.backend.openemswebsocket.provider.internal; import java.util.Optional; @@ -6,7 +6,7 @@ import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.device.MetadataDevices; +import io.openems.backend.metadata.api.MetadataDevices; import io.openems.common.session.SessionData; public class OpenemsSessionData extends SessionData { diff --git a/backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSessionManager.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java similarity index 95% rename from backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSessionManager.java rename to io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java index 6d53141c86e..58127d36782 100644 --- a/backend/src/main/java/io/openems/backend/openemswebsocket/session/OpenemsSessionManager.java +++ b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java @@ -1,4 +1,4 @@ -package io.openems.backend.openemswebsocket.session; +package io.openems.backend.openemswebsocket.provider.internal; import java.util.Map.Entry; import java.util.Optional; diff --git a/backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocketSingleton.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java similarity index 87% rename from backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocketSingleton.java rename to io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java index 1d1656d25b2..d0fe02880c6 100644 --- a/backend/src/main/java/io/openems/backend/openemswebsocket/OpenemsWebsocketSingleton.java +++ b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java @@ -1,333 +1,347 @@ -package io.openems.backend.openemswebsocket; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.handshake.ClientHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.backend.browserwebsocket.BrowserWebsocket; -import io.openems.backend.metadata.Metadata; -import io.openems.backend.metadata.api.device.MetadataDevice; -import io.openems.backend.metadata.api.device.MetadataDevices; -import io.openems.backend.openemswebsocket.session.OpenemsSession; -import io.openems.backend.openemswebsocket.session.OpenemsSessionData; -import io.openems.backend.openemswebsocket.session.OpenemsSessionManager; -import io.openems.backend.timedata.Timedata; -import io.openems.backend.utilities.StringUtils; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.utils.JsonUtils; -import io.openems.common.websocket.AbstractWebsocketServer; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.WebSocketUtils; - -/** - * Handles connections to OpenEMS-Devices. - * - * @author stefan.feilmeier - * - */ -public class OpenemsWebsocketSingleton - extends AbstractWebsocketServer { - private final Logger log = LoggerFactory.getLogger(OpenemsWebsocketSingleton.class); - - protected OpenemsWebsocketSingleton(int port) { - super(port, new OpenemsSessionManager()); - } - - /** - * Open event of websocket. Parses the "apikey" and stores it in a new Session. - */ - @Override - protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - String apikey = ""; - Set deviceNames = new HashSet<>(); - try { - - // get apikey from handshake - Optional apikeyOpt = parseApikeyFromHandshake(handshake); - if (!apikeyOpt.isPresent()) { - throw new OpenemsException("Apikey is missing in handshake"); - } - apikey = apikeyOpt.get(); - - // if existing: close existing websocket for this apikey - Optional oldSessionOpt = this.sessionManager.getSessionByToken(apikey); - if (oldSessionOpt.isPresent()) { - OpenemsSession oldSession = oldSessionOpt.get(); - WebSocket oldWebsocket = oldSession.getData().getWebsocket(); - oldWebsocket.closeConnection(CloseFrame.REFUSE, - "Another device with this apikey [" + apikey + "] connected."); - this.sessionManager.removeSession(oldSession); - } - - // get device for apikey - MetadataDevices devices = Metadata.instance().getDeviceModel().getDevicesForApikey(apikey); - if (devices.isEmpty()) { - throw new OpenemsException("Unable to find device for apikey [" + apikey + "]"); - } - deviceNames = devices.getNames(); - - // create new session - OpenemsSessionData sessionData = new OpenemsSessionData(websocket, devices); - OpenemsSession session = sessionManager.createNewSession(apikey, sessionData); - - // send successful reply to openems - JsonObject jReply = DefaultMessages.openemsConnectionSuccessfulReply(); - WebSocketUtils.send(websocket, jReply); - // add websocket to local cache - this.addWebsocket(websocket, session); - - log.info("Device [" + String.join(",", deviceNames) + "] connected."); - - try { - // set device active (in Odoo) - for (MetadataDevice device : devices) { - if (device.getState().equals("inactive")) { - device.setState("active"); - } - device.setLastMessage(); - device.writeObject(); - } - } catch (OpenemsException e) { - // this error does not stop the connection - log.error("Device [" + String.join(",", deviceNames) + "] error: " + e.getMessage()); - } - - // announce browserWebsocket that this OpenEMS Edge was connected - BrowserWebsocket.instance().openemsConnectionOpened(deviceNames); - - } catch (OpenemsException e) { - // send connection failed to OpenEMS - JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); - WebSocketUtils.send(websocket, jReply); - // close websocket - websocket.closeConnection(CloseFrame.REFUSE, "OpenEMS connection failed. Device [" - + String.join(",", deviceNames) + "] Apikey [" + apikey + "]"); - } - } - - /** - * Close event of websocket. Removes the session. - */ - @Override - public void _onClose(WebSocket websocket, Optional sessionOpt) { - if (sessionOpt.isPresent()) { - // log.info("Would remove the session... " + sessionOpt.get()); - sessionManager.removeSession(sessionOpt.get()); - } - } - - /** - * Message event of websocket. Handles a new message. At this point the device is already authenticated. - */ - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - OpenemsSessionData sessionData = this.getSessionFromWebsocket(websocket).get().getData(); - MetadataDevices devices = sessionData.getDevices(); - - // if (!jMessage.has("timedata") && !jMessage.has("currentData") && !jMessage.has("log") - // && !jMessage.has("config")) { - // log.info("Received from " + device.getName() + ": " + jMessage.toString()); - // } - - /* - * Config? -> store in Metadata - */ - if (jMessage.has("config")) { - try { - JsonObject jConfig = JsonUtils.getAsJsonObject(jMessage, "config"); - for (MetadataDevice device : devices) { - device.setOpenemsConfig(jConfig); - } - sessionData.setOpenemsConfig(jConfig); - log.info("Device [" + devices.getNamesString() + "] sent config."); - } catch (OpenemsException e) { - log.error(e.getMessage()); - } - } - - /* - * Is this a reply? -> forward to Browser - */ - if (jMessage.has("id")) { - for (String deviceName : devices.getNames()) { - forwardReplyToBrowser(websocket, deviceName, jMessage); - } - } - - /* - * New timestamped data - */ - if (jMessage.has("timedata")) { - timedata(devices, jMessage.get("timedata")); - } - - // Save data to Odoo - try { - for (MetadataDevice device : devices) { - device.writeObject(); - } - } catch (OpenemsException e) { - log.error("Device [" + devices.getNamesString() + "] error: " + e.getMessage()); - } - } - - private void forwardReplyToBrowser(WebSocket openemsWebsocket, String deviceName, JsonObject jMessage) { - try { - // get browser websocket - JsonArray jId = JsonUtils.getAsJsonArray(jMessage, "id"); - String token = JsonUtils.getAsString(jId.get(jId.size() - 1)); - Optional browserWebsocketOpt = BrowserWebsocket.instance().getWebsocketByToken(token); - if (!browserWebsocketOpt.isPresent()) { - log.warn("Device [" + deviceName + "] Browser websocket is not connected. Message [" - + StringUtils.toShortString(jMessage, 100) + "]"); - if (jMessage.has("currentData")) { - // unsubscribe obsolete browser websocket - WebSocketUtils.send(openemsWebsocket, DefaultMessages.currentDataSubscribe(jId, new JsonObject())); - } - if (jMessage.has("log")) { - // unsubscribe obsolete browser websocket - WebSocketUtils.send(openemsWebsocket, DefaultMessages.logUnsubscribe(jId)); - } - return; - } - WebSocket browserWebsocket = browserWebsocketOpt.get(); - - // remove token from message id - jId.remove(jId.size() - 1); - jMessage.add("id", jId); - // always add device name - jMessage.addProperty("device", deviceName); - - // send - WebSocketUtils.send(browserWebsocket, jMessage); - } catch (OpenemsException e) { - log.error("Device [" + deviceName + "] error: " + e.getMessage()); - } - } - - private void timedata(MetadataDevices devices, JsonElement jTimedataElement) { - try { - JsonObject jTimedata = JsonUtils.getAsJsonObject(jTimedataElement); - // Write to InfluxDB - try { - Timedata.instance().write(devices, jTimedata); - log.debug(devices.getNamesString() + ": wrote " + jTimedata.entrySet().size() + " timestamps " - + StringUtils.toShortString(jTimedata, 120)); - } catch (Exception e) { - log.error("Unable to write Timedata: ", e); - } - // Set Odoo last message timestamp - for (MetadataDevice device : devices) { - device.setLastMessage(); - } - - for (Entry jTimedataEntry : jTimedata.entrySet()) { - try { - JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue()); - - // set Odoo last update timestamp only for those channels - for (String channel : jChannels.keySet()) { - if (channel.endsWith("ActivePower") - || channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2") - | channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) { - for (MetadataDevice device : devices) { - device.setLastUpdate(); - } - } - } - - // set specific Odoo values - if (jChannels.has("ess0/Soc")) { - int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt(); - for (MetadataDevice device : devices) { - device.setSoc(soc); - } - } - if (jChannels.has("system0/PrimaryIpAddress")) { - String ipv4 = JsonUtils.getAsPrimitive(jChannels, "system0/PrimaryIpAddress").getAsString(); - for (MetadataDevice device : devices) { - device.setIpV4(ipv4); - } - } - } catch (OpenemsException e) { - log.error("Device [" + String.join(",", devices.getNames()) + "] error: " + e.getMessage()); - } - } - - } catch (OpenemsException e) { - log.error("Device [" + devices.getNamesString() + "] error: " + e.getMessage()); - } - } - - /** - * Parses the apikey from websocket onOpen handshake - * - * @param handshake - * @return - */ - private Optional parseApikeyFromHandshake(ClientHandshake handshake) { - if (handshake.hasFieldValue("apikey")) { - String apikey = handshake.getFieldValue("apikey"); - return Optional.ofNullable(apikey); - } - return Optional.empty(); - } - - /** - * Returns true if this device is currently connected - * - * @param name - * @return - */ - public Optional getOpenemsSession(String deviceName) { - return this.sessionManager.getSessionByDeviceName(deviceName); - } - - /** - * Returns true if this device is currently connected - * - * @param name - * @return - */ - public boolean isOpenemsWebsocketConnected(String deviceName) { - Optional sessionOpt = getOpenemsSession(deviceName); - if (sessionOpt.isPresent()) { - return true; - } else { - return false; - } - } - - /** - * Returns the OpenemsWebsocket for the given device - * - * @param name - * @return - */ - public Optional getOpenemsWebsocket(String deviceName) { - Optional sessionOpt = this.sessionManager.getSessionByDeviceName(deviceName); - if (!sessionOpt.isPresent()) { - return Optional.empty(); - } - OpenemsSession session = sessionOpt.get(); - return this.getWebsocketFromSession(session); - } - - public Collection getSessions() { - return Collections.synchronizedCollection(this.sessionManager.getSessions()); - } +package io.openems.backend.openemswebsocket.provider.internal; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; + +import org.java_websocket.WebSocket; +import org.java_websocket.framing.CloseFrame; +import org.java_websocket.handshake.ClientHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; +import io.openems.backend.metadata.api.MetadataDevice; +import io.openems.backend.metadata.api.MetadataDevices; +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.timedata.api.TimedataService; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; +import io.openems.common.websocket.AbstractWebsocketServer; +import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.WebSocketUtils; + +/** + * Handles connections to OpenEMS-Devices. + * + * @author stefan.feilmeier + * + */ +public class OpenemsWebsocket + extends AbstractWebsocketServer { + private final Logger log = LoggerFactory.getLogger(OpenemsWebsocket.class); + private MetadataService metadataService; + private BrowserWebsocketService browserWebsocketService; + private TimedataService timedataService; + + public OpenemsWebsocket(int port) { + super(port, new OpenemsSessionManager()); + } + + /** + * Open event of websocket. Parses the "apikey" and stores it in a new Session. + */ + @Override + protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + String apikey = ""; + Set deviceNames = new HashSet<>(); + try { + + // get apikey from handshake + Optional apikeyOpt = parseApikeyFromHandshake(handshake); + if (!apikeyOpt.isPresent()) { + throw new OpenemsException("Apikey is missing in handshake"); + } + apikey = apikeyOpt.get(); + + // if existing: close existing websocket for this apikey + Optional oldSessionOpt = this.sessionManager.getSessionByToken(apikey); + if (oldSessionOpt.isPresent()) { + OpenemsSession oldSession = oldSessionOpt.get(); + WebSocket oldWebsocket = oldSession.getData().getWebsocket(); + oldWebsocket.closeConnection(CloseFrame.REFUSE, + "Another device with this apikey [" + apikey + "] connected."); + this.sessionManager.removeSession(oldSession); + } + + // get device for apikey + MetadataDevices devices = this.metadataService.getDeviceModel().getDevicesForApikey(apikey); + if (devices.isEmpty()) { + throw new OpenemsException("Unable to find device for apikey [" + apikey + "]"); + } + deviceNames = devices.getNames(); + + // create new session + OpenemsSessionData sessionData = new OpenemsSessionData(websocket, devices); + OpenemsSession session = sessionManager.createNewSession(apikey, sessionData); + + // send successful reply to openems + JsonObject jReply = DefaultMessages.openemsConnectionSuccessfulReply(); + WebSocketUtils.send(websocket, jReply); + // add websocket to local cache + this.addWebsocket(websocket, session); + + log.info("Device [" + String.join(",", deviceNames) + "] connected."); + + try { + // set device active (in Odoo) + for (MetadataDevice device : devices) { + if (device.getState().equals("inactive")) { + device.setState("active"); + } + device.setLastMessage(); + device.writeObject(); + } + } catch (OpenemsException e) { + // this error does not stop the connection + log.error("Device [" + String.join(",", deviceNames) + "] error: " + e.getMessage()); + } + + // announce browserWebsocket that this OpenEMS Edge was connected + this.browserWebsocketService.openemsConnectionOpened(deviceNames); + + } catch (OpenemsException e) { + // send connection failed to OpenEMS + JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); + WebSocketUtils.send(websocket, jReply); + // close websocket + websocket.closeConnection(CloseFrame.REFUSE, "OpenEMS connection failed. Device [" + + String.join(",", deviceNames) + "] Apikey [" + apikey + "]"); + } + } + + /** + * Close event of websocket. Removes the session. + */ + @Override + public void _onClose(WebSocket websocket, Optional sessionOpt) { + if (sessionOpt.isPresent()) { + // log.info("Would remove the session... " + sessionOpt.get()); + sessionManager.removeSession(sessionOpt.get()); + } + } + + /** + * Message event of websocket. Handles a new message. At this point the device + * is already authenticated. + */ + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, + Optional deviceNameOpt) { + OpenemsSessionData sessionData = this.getSessionFromWebsocket(websocket).get().getData(); + MetadataDevices devices = sessionData.getDevices(); + + // if (!jMessage.has("timedata") && !jMessage.has("currentData") && + // !jMessage.has("log") + // && !jMessage.has("config")) { + // log.info("Received from " + device.getName() + ": " + jMessage.toString()); + // } + + /* + * Config? -> store in Metadata + */ + if (jMessage.has("config")) { + try { + JsonObject jConfig = JsonUtils.getAsJsonObject(jMessage, "config"); + for (MetadataDevice device : devices) { + device.setOpenemsConfig(jConfig); + } + sessionData.setOpenemsConfig(jConfig); + log.info("Device [" + devices.getNamesString() + "] sent config."); + } catch (OpenemsException e) { + log.error(e.getMessage()); + } + } + + /* + * Is this a reply? -> forward to Browser + */ + if (jMessage.has("id")) { + for (String deviceName : devices.getNames()) { + forwardReplyToBrowser(websocket, deviceName, jMessage); + } + } + + /* + * New timestamped data + */ + if (jMessage.has("timedata")) { + timedata(devices, jMessage.get("timedata")); + } + + // Save data to Odoo + try { + for (MetadataDevice device : devices) { + device.writeObject(); + } + } catch (OpenemsException e) { + log.error("Device [" + devices.getNamesString() + "] error: " + e.getMessage()); + } + } + + private void forwardReplyToBrowser(WebSocket openemsWebsocket, String deviceName, JsonObject jMessage) { + try { + // get browser websocket + JsonArray jId = JsonUtils.getAsJsonArray(jMessage, "id"); + String token = JsonUtils.getAsString(jId.get(jId.size() - 1)); + Optional browserWebsocketOpt = this.browserWebsocketService.getWebsocketByToken(token); + if (!browserWebsocketOpt.isPresent()) { + log.warn("Device [" + deviceName + "] Browser websocket is not connected. Message [" + + StringUtils.toShortString(jMessage, 100) + "]"); + if (jMessage.has("currentData")) { + // unsubscribe obsolete browser websocket + WebSocketUtils.send(openemsWebsocket, DefaultMessages.currentDataSubscribe(jId, new JsonObject())); + } + if (jMessage.has("log")) { + // unsubscribe obsolete browser websocket + WebSocketUtils.send(openemsWebsocket, DefaultMessages.logUnsubscribe(jId)); + } + return; + } + WebSocket browserWebsocket = browserWebsocketOpt.get(); + + // remove token from message id + jId.remove(jId.size() - 1); + jMessage.add("id", jId); + // always add device name + jMessage.addProperty("device", deviceName); + + // send + WebSocketUtils.send(browserWebsocket, jMessage); + } catch (OpenemsException e) { + log.error("Device [" + deviceName + "] error: " + e.getMessage()); + } + } + + private void timedata(MetadataDevices devices, JsonElement jTimedataElement) { + try { + JsonObject jTimedata = JsonUtils.getAsJsonObject(jTimedataElement); + // Write to InfluxDB + try { + this.timedataService.write(devices, jTimedata); + log.debug(devices.getNamesString() + ": wrote " + jTimedata.entrySet().size() + " timestamps " + + StringUtils.toShortString(jTimedata, 120)); + } catch (Exception e) { + log.error("Unable to write Timedata: ", e); + } + // Set Odoo last message timestamp + for (MetadataDevice device : devices) { + device.setLastMessage(); + } + + for (Entry jTimedataEntry : jTimedata.entrySet()) { + try { + JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue()); + + // set Odoo last update timestamp only for those channels + for (String channel : jChannels.keySet()) { + if (channel.endsWith("ActivePower") + || channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2") + | channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) { + for (MetadataDevice device : devices) { + device.setLastUpdate(); + } + } + } + + // set specific Odoo values + if (jChannels.has("ess0/Soc")) { + int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt(); + for (MetadataDevice device : devices) { + device.setSoc(soc); + } + } + if (jChannels.has("system0/PrimaryIpAddress")) { + String ipv4 = JsonUtils.getAsPrimitive(jChannels, "system0/PrimaryIpAddress").getAsString(); + for (MetadataDevice device : devices) { + device.setIpV4(ipv4); + } + } + } catch (OpenemsException e) { + log.error("Device [" + String.join(",", devices.getNames()) + "] error: " + e.getMessage()); + } + } + + } catch (OpenemsException e) { + log.error("Device [" + devices.getNamesString() + "] error: " + e.getMessage()); + } + } + + /** + * Parses the apikey from websocket onOpen handshake + * + * @param handshake + * @return + */ + private Optional parseApikeyFromHandshake(ClientHandshake handshake) { + if (handshake.hasFieldValue("apikey")) { + String apikey = handshake.getFieldValue("apikey"); + return Optional.ofNullable(apikey); + } + return Optional.empty(); + } + + /** + * Returns true if this device is currently connected + * + * @param name + * @return + */ + public Optional getOpenemsSession(String deviceName) { + return this.sessionManager.getSessionByDeviceName(deviceName); + } + + /** + * Returns true if this device is currently connected + * + * @param name + * @return + */ + public boolean isOpenemsWebsocketConnected(String deviceName) { + Optional sessionOpt = getOpenemsSession(deviceName); + if (sessionOpt.isPresent()) { + return true; + } else { + return false; + } + } + + /** + * Returns the OpenemsWebsocket for the given device + * + * @param name + * @return + */ + public Optional getOpenemsWebsocket(String deviceName) { + Optional sessionOpt = this.sessionManager.getSessionByDeviceName(deviceName); + if (!sessionOpt.isPresent()) { + return Optional.empty(); + } + OpenemsSession session = sessionOpt.get(); + return this.getWebsocketFromSession(session); + } + + public Collection getSessions() { + return Collections.synchronizedCollection(this.sessionManager.getSessions()); + } + + public void setMetadataService(MetadataService metadataService) { + this.metadataService = metadataService; + } + + public void setBrowserWebsocketService(BrowserWebsocketService browserWebsocketService) { + this.browserWebsocketService = browserWebsocketService; + } + + public void setTimedataService(TimedataService timedataService) { + this.timedataService = timedataService; + } } \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java new file mode 100644 index 00000000000..ac359d6e939 --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.openemswebsocket.provider; diff --git a/io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java b/io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java new file mode 100644 index 00000000000..b336a053e9a --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java @@ -0,0 +1,26 @@ +package io.openems.backend.openemswebsocket.impl; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.openems.backend.openemswebsocket.provider.OpenemsWebsocketProvider; + +/* + * Example JUNit test case + * + */ + +public class ProviderImplTest { + + /* + * Example test method + */ + + @Test + public void simple() { + OpenemsWebsocketProvider impl = new OpenemsWebsocketProvider(); + assertNotNull(impl); + } + +} diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index 686b199ddfe..3a8aeb4c757 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -1,12 +1,15 @@ package io.openems.backend.timedata.api; +import java.time.ZonedDateTime; import java.util.Optional; import org.osgi.annotation.versioning.ProviderType; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.openems.backend.metadata.api.MetadataDevices; +import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @ProviderType @@ -30,4 +33,29 @@ public interface TimedataService { public void write(MetadataDevices devices, JsonObject jData); public Optional getChannelValue(int deviceId, ChannelAddress channelAddress); + + /** + * Queries the database and returns a JsonArray of the form + * + *
    +	 *	[{
    +	 *  	timestamp: "2017-03-21T08:55:20Z",
    +	 *  	channels: {
    +	 *			'thing': {
    +	 *				'channel': 'value'
    +	 *			}
    +	 *		}
    +	 * 	}]
    +	 * 
    + * + * @param deviceId + * @param fromDate + * @param toDate + * @param channels + * @param resolution + * @return + * @throws OpenemsException + */ + public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + int resolution/* , JsonObject kWh */) throws OpenemsException; } diff --git a/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs index 36bab44257a..6e0123932e9 100644 --- a/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,5 @@ eclipse.preferences.version=1 -encoding//src/io/openems/backend/timedata/influx/api/InfluxImpl.java=UTF-8 +encoding//src/io/openems/backend/timedata/influx/InfluxProvider.java=UTF-8 encoding//test/io/openems/backend/timedata/influx/ProviderImplTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/debug.bndrun=UTF-8 diff --git a/io.openems.backend.timedata.influx/bnd.bnd b/io.openems.backend.timedata.influx/bnd.bnd index a5dea07bceb..f8b5e96718c 100644 --- a/io.openems.backend.timedata.influx/bnd.bnd +++ b/io.openems.backend.timedata.influx/bnd.bnd @@ -4,8 +4,7 @@ Bundle-Version: 1.0.0.${tstamp} -Export-Package: \ - io.openems.backend.timedata.influx.api;-provide=true +Export-Package: io.openems.backend.timedata.influx;-provide=true Private-Package: io.openems.backend.timedata.influx.internal diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java similarity index 91% rename from io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java rename to io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java index 225b011dd30..1edbb33827d 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/api/InfluxImpl.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java @@ -1,5 +1,6 @@ -package io.openems.backend.timedata.influx.api; +package io.openems.backend.timedata.influx; +import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -14,10 +15,12 @@ import org.influxdb.dto.Point.Builder; 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.metatype.annotations.ObjectClassDefinition; import com.google.common.collect.TreeBasedTable; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -25,15 +28,16 @@ import io.openems.backend.metadata.api.MetadataDevices; import io.openems.backend.timedata.api.TimedataService; import io.openems.backend.timedata.influx.internal.DeviceCache; +import io.openems.backend.timedata.influx.internal.InfluxdbUtils; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; import org.osgi.service.metatype.annotations.Designate; -@Designate(ocd = InfluxImpl.Config.class, factory = false) -@Component(name = "io.openems.backend.timedata.influx") -public class InfluxImpl implements TimedataService { +@Designate(ocd = InfluxProvider.Config.class, factory = false) +@Component(name = "InfluxDB", configurationPolicy = ConfigurationPolicy.REQUIRE) +public class InfluxProvider implements TimedataService { private final String TMP_MINI_MEASUREMENT = "minies"; @@ -73,15 +77,17 @@ void activate(Config config) throws OpenemsException { this.password = config.password(); this.measurement = config.measurement(); // TODO: connect asynchronously to not block the activator -// try { -// this.connect(); -// } catch (Exception e) { -// throw new OpenemsException("Connecting to InfluxDB failed: " + e.getMessage()); -// } + // try { + // this.connect(); + // } catch (Exception e) { + // throw new OpenemsException("Connecting to InfluxDB failed: " + + // e.getMessage()); + // } } @Deactivate void deactivate() { + System.out.println("Deactivate InfluxDB"); } private void connect() throws Exception { @@ -163,7 +169,8 @@ public void write(MetadataDevices devices, JsonObject jData) { if (cacheTimestamp != 0l) { System.out.println("Invalidate cache for device [" + deviceId + "]. This timestamp [" + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); - // TODO log.info("Invalidate cache for device [" + deviceId + "]. This timestamp [" + + // TODO log.info("Invalidate cache for device [" + deviceId + "]. This timestamp + // [" + // timestamp // + "]. Cache timestamp [" + cacheTimestamp + "]"); } @@ -342,14 +349,12 @@ private Optional parseValue(String channel, Object value) { return Optional.empty(); } - // @Override - // public JsonArray queryHistoricData(Optional deviceIdOpt, - // ZonedDateTime fromDate, ZonedDateTime toDate, - // JsonObject channels, int resolution) throws OpenemsException { - // return InfluxdbUtils.queryHistoricData(influxDB, this.database, deviceIdOpt, - // fromDate, toDate, channels, - // resolution); - // } + @Override + public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, + JsonObject channels, int resolution) throws OpenemsException { + return InfluxdbUtils.queryHistoricData(influxDB, this.database, deviceIdOpt, fromDate, toDate, channels, + resolution); + } @Override public Optional getChannelValue(int deviceId, ChannelAddress channelAddress) { diff --git a/common/src/io/openems/common/utils/InfluxdbUtils.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/InfluxdbUtils.java similarity index 96% rename from common/src/io/openems/common/utils/InfluxdbUtils.java rename to io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/InfluxdbUtils.java index 1ced5cf28b9..7a881b121bd 100644 --- a/common/src/io/openems/common/utils/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/InfluxdbUtils.java @@ -1,311 +1,312 @@ -package io.openems.common.utils; - -import java.time.Instant; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import org.influxdb.InfluxDB; -import org.influxdb.dto.Query; -import org.influxdb.dto.QueryResult; -import org.influxdb.dto.QueryResult.Result; -import org.influxdb.dto.QueryResult.Series; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.ChannelAddress; - -public class InfluxdbUtils { - - public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional deviceId, - ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { - // Prepare query string - StringBuilder query = new StringBuilder("SELECT "); - query.append(toChannelAddressList(channels)); - query.append(" FROM data WHERE "); - if (deviceId.isPresent()) { - query.append("fems = '"); - query.append(deviceId.get()); - query.append("' AND "); - } - query.append("time > "); - query.append(String.valueOf(fromDate.toEpochSecond())); - query.append("s"); - query.append(" AND time < "); - query.append(String.valueOf(toDate.toEpochSecond())); - query.append("s"); - query.append(" GROUP BY time("); - query.append(resolution); - query.append("s) fill(null)"); - - QueryResult queryResult = executeQuery(influxdb, database, query.toString()); - - JsonArray j = new JsonArray(); - for (Result result : queryResult.getResults()) { - List seriess = result.getSeries(); - if (seriess != null) { - for (Series series : seriess) { - // create thing/channel index - ArrayList addressIndex = new ArrayList<>(); - for (String column : series.getColumns()) { - if (column.equals("time")) { - continue; - } - addressIndex.add(ChannelAddress.fromString(column)); - } - // first: create empty timestamp objects - for (List values : series.getValues()) { - JsonObject jTimestamp = new JsonObject(); - // get timestamp - Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) values.get(0)).doubleValue()); - ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); - String timestampString = timestamp.format(DateTimeFormatter.ISO_INSTANT); - jTimestamp.addProperty("time", timestampString); - // add empty channels by copying "channels" parameter - JsonObject jChannels = new JsonObject(); - for (Entry entry : channels.entrySet()) { - String thingId = entry.getKey(); - JsonObject jThing = new JsonObject(); - JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement channelElement : channelIds) { - String channelId = JsonUtils.getAsString(channelElement); - jThing.add(channelId, JsonNull.INSTANCE); - } - jChannels.add(thingId, jThing); - } - jTimestamp.add("channels", jChannels); - j.add(jTimestamp); - } - // then: add all data - for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { - for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { - Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); - ChannelAddress address = addressIndex.get(columnIndex - 1); - j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() - .get(address.getThingId()).getAsJsonObject() - .addProperty(address.getChannelId(), value); - } - } - } - } - } - return j; - } - -// private static JsonObject querykWh(InfluxDB influxdb, String database, Optional fems, -// ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh) -// throws OpenemsException { -// JsonArray gridThing = getGridThing(kWh); -// JsonArray storageThing = getStorageThing(kWh); -// JsonArray things = new JsonArray(); -// things.addAll(storageThing); -// things.addAll(gridThing); -// -// JsonObject jThing = new JsonObject(); -// ArrayList productionChannels = toChannelAddressListAvg(channels, things); -// -// for (int i = 0; i < productionChannels.size(); i++) { -// /* -// * SUM data -// */ -// StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); -// query.append(productionChannels.get(i)); -// query.append("\") AS AP FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time > "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// query.append(" AND time < "); -// query.append(String.valueOf(toDate.toEpochSecond())); -// query.append("s"); -// query.append(" GROUP BY time(1s) fill(previous))"); -// -// QueryResult queryResult = executeQuery(influxdb, database, query.toString()); -// -// Double sumProduction = 0.0; -// try { -// for (Result result : queryResult.getResults()) { -// for (Series serie : result.getSeries()) { -// for (List l : serie.getValues()) { -// sumProduction = (Double) l.get(1); -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing SUM production: " + e); -// } -// -// /* -// * FIRST production data -// */ -// query = new StringBuilder("SELECT FIRST(\""); -// query.append(productionChannels.get(i)); -// query.append("\") FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time > "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// query.append(" AND time < "); -// query.append(String.valueOf(toDate.toEpochSecond())); -// query.append("s"); -// -// queryResult = executeQuery(influxdb, database, query.toString()); -// -// int second = 0; -// try { -// for (Result result : queryResult.getResults()) { -// for (Series serie : result.getSeries()) { -// for (List l : serie.getValues()) { -// Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); -// ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); -// if (timestamp.equals(fromDate)) { -// System.out.println("Parsing FIRST: nothing null"); -// } else { -// second = timestamp.getSecond(); -// } -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing FIRST production: " + e); -// } -// -// /* -// * LAST data -// */ -// query = new StringBuilder("SELECT LAST(\""); -// query.append(productionChannels.get(i)); -// query.append("\") FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time < "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// -// queryResult = executeQuery(influxdb, query.toString(), database); -// -// try { -// if (queryResult.getResults() != null) { -// for (Result result : queryResult.getResults()) { -// if (result.getSeries() != null) { -// for (Series serie : result.getSeries()) { -// if (serie.getValues() != null) { -// for (List l : serie.getValues()) { -// if (l.get(1) != null) { -// sumProduction += (Double) l.get(1) * second; -// } -// } -// } -// } -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing LAST production: " + e); -// } -// -// Double avg = sumProduction / 3600 / 1000; -// -// JsonObject element = new JsonObject(); -// element.addProperty("value", avg); -// element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); -// jThing.add(productionChannels.get(i).toString(), element); -// } -// -// return jThing; -// } - -// private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { -// JsonArray gridThing = new JsonArray(); -// for (Entry entry : kWh.entrySet()) { -// String thingId = entry.getKey(); -// if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { -// gridThing.add(thingId); -// } -// } -// return gridThing; -// } -// -// private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { -// JsonArray storageThing = new JsonArray(); -// for (Entry entry : kWh.entrySet()) { -// String thingId = entry.getKey(); -// if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { -// storageThing.add(thingId); -// } -// } -// return storageThing; -// } -// -// private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) -// throws OpenemsException { -// ArrayList channelAddresses = new ArrayList<>(); -// for (Entry entry : channels.entrySet()) { -// String thingId = entry.getKey(); -// JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); -// for (JsonElement channelElement : channelIds) { -// String channelId = JsonUtils.getAsString(channelElement); -// if (channelId.contains("ActivePower")) { -// String name = thingId + "/" + channelId; -// boolean isGridOrStorage = false; -// for (int i = 0; i < things.size(); i++) { -// if (JsonUtils.getAsString(things.get(i)).equals(name)) { -// isGridOrStorage = true; -// } -// } -// if (!isGridOrStorage) { -// channelAddresses.add(thingId + "/" + channelId); -// } -// } -// } -// } -// return channelAddresses; -// } - - private static String toChannelAddressList(JsonObject channels) throws OpenemsException { - ArrayList channelAddresses = new ArrayList<>(); - for (Entry entry : channels.entrySet()) { - String thingId = entry.getKey(); - JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement channelElement : channelIds) { - String channelId = JsonUtils.getAsString(channelElement); - channelAddresses - .add("MEAN(\"" + thingId + "/" + channelId + "\") AS \"" + thingId + "/" + channelId + "\""); - } - } - return String.join(", ", channelAddresses); - } - - private static QueryResult executeQuery(InfluxDB influxdb, String database, String query) throws OpenemsException { - // Parse result - QueryResult queryResult; - try { - queryResult = influxdb.query(new Query(query, database), TimeUnit.MILLISECONDS); - } catch (RuntimeException e) { - throw new OpenemsException("InfluxDB query runtime error. Query: " + query + ", Error: " + e.getMessage()); - } - if (queryResult.hasError()) { - throw new OpenemsException("InfluxDB query error. Query: " + query + ", Error: " + queryResult.getError()); - } - return queryResult; - } -} +package io.openems.backend.timedata.influx.internal; + +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.influxdb.InfluxDB; +import org.influxdb.dto.Query; +import org.influxdb.dto.QueryResult; +import org.influxdb.dto.QueryResult.Result; +import org.influxdb.dto.QueryResult.Series; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; + +public class InfluxdbUtils { + + public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional deviceId, + ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { + // Prepare query string + StringBuilder query = new StringBuilder("SELECT "); + query.append(toChannelAddressList(channels)); + query.append(" FROM data WHERE "); + if (deviceId.isPresent()) { + query.append("fems = '"); + query.append(deviceId.get()); + query.append("' AND "); + } + query.append("time > "); + query.append(String.valueOf(fromDate.toEpochSecond())); + query.append("s"); + query.append(" AND time < "); + query.append(String.valueOf(toDate.toEpochSecond())); + query.append("s"); + query.append(" GROUP BY time("); + query.append(resolution); + query.append("s) fill(null)"); + + QueryResult queryResult = executeQuery(influxdb, database, query.toString()); + + JsonArray j = new JsonArray(); + for (Result result : queryResult.getResults()) { + List seriess = result.getSeries(); + if (seriess != null) { + for (Series series : seriess) { + // create thing/channel index + ArrayList addressIndex = new ArrayList<>(); + for (String column : series.getColumns()) { + if (column.equals("time")) { + continue; + } + addressIndex.add(ChannelAddress.fromString(column)); + } + // first: create empty timestamp objects + for (List values : series.getValues()) { + JsonObject jTimestamp = new JsonObject(); + // get timestamp + Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) values.get(0)).doubleValue()); + ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); + String timestampString = timestamp.format(DateTimeFormatter.ISO_INSTANT); + jTimestamp.addProperty("time", timestampString); + // add empty channels by copying "channels" parameter + JsonObject jChannels = new JsonObject(); + for (Entry entry : channels.entrySet()) { + String thingId = entry.getKey(); + JsonObject jThing = new JsonObject(); + JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement channelElement : channelIds) { + String channelId = JsonUtils.getAsString(channelElement); + jThing.add(channelId, JsonNull.INSTANCE); + } + jChannels.add(thingId, jThing); + } + jTimestamp.add("channels", jChannels); + j.add(jTimestamp); + } + // then: add all data + for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { + for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { + Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); + ChannelAddress address = addressIndex.get(columnIndex - 1); + j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() + .get(address.getThingId()).getAsJsonObject() + .addProperty(address.getChannelId(), value); + } + } + } + } + } + return j; + } + +// private static JsonObject querykWh(InfluxDB influxdb, String database, Optional fems, +// ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh) +// throws OpenemsException { +// JsonArray gridThing = getGridThing(kWh); +// JsonArray storageThing = getStorageThing(kWh); +// JsonArray things = new JsonArray(); +// things.addAll(storageThing); +// things.addAll(gridThing); +// +// JsonObject jThing = new JsonObject(); +// ArrayList productionChannels = toChannelAddressListAvg(channels, things); +// +// for (int i = 0; i < productionChannels.size(); i++) { +// /* +// * SUM data +// */ +// StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); +// query.append(productionChannels.get(i)); +// query.append("\") AS AP FROM data WHERE "); +// if (fems.isPresent()) { +// query.append("fems = '"); +// query.append(fems.get()); +// query.append("' AND "); +// } +// query.append("time > "); +// query.append(String.valueOf(fromDate.toEpochSecond())); +// query.append("s"); +// query.append(" AND time < "); +// query.append(String.valueOf(toDate.toEpochSecond())); +// query.append("s"); +// query.append(" GROUP BY time(1s) fill(previous))"); +// +// QueryResult queryResult = executeQuery(influxdb, database, query.toString()); +// +// Double sumProduction = 0.0; +// try { +// for (Result result : queryResult.getResults()) { +// for (Series serie : result.getSeries()) { +// for (List l : serie.getValues()) { +// sumProduction = (Double) l.get(1); +// } +// } +// } +// } catch (Exception e) { +// System.out.println("Error parsing SUM production: " + e); +// } +// +// /* +// * FIRST production data +// */ +// query = new StringBuilder("SELECT FIRST(\""); +// query.append(productionChannels.get(i)); +// query.append("\") FROM data WHERE "); +// if (fems.isPresent()) { +// query.append("fems = '"); +// query.append(fems.get()); +// query.append("' AND "); +// } +// query.append("time > "); +// query.append(String.valueOf(fromDate.toEpochSecond())); +// query.append("s"); +// query.append(" AND time < "); +// query.append(String.valueOf(toDate.toEpochSecond())); +// query.append("s"); +// +// queryResult = executeQuery(influxdb, database, query.toString()); +// +// int second = 0; +// try { +// for (Result result : queryResult.getResults()) { +// for (Series serie : result.getSeries()) { +// for (List l : serie.getValues()) { +// Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); +// ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); +// if (timestamp.equals(fromDate)) { +// System.out.println("Parsing FIRST: nothing null"); +// } else { +// second = timestamp.getSecond(); +// } +// } +// } +// } +// } catch (Exception e) { +// System.out.println("Error parsing FIRST production: " + e); +// } +// +// /* +// * LAST data +// */ +// query = new StringBuilder("SELECT LAST(\""); +// query.append(productionChannels.get(i)); +// query.append("\") FROM data WHERE "); +// if (fems.isPresent()) { +// query.append("fems = '"); +// query.append(fems.get()); +// query.append("' AND "); +// } +// query.append("time < "); +// query.append(String.valueOf(fromDate.toEpochSecond())); +// query.append("s"); +// +// queryResult = executeQuery(influxdb, query.toString(), database); +// +// try { +// if (queryResult.getResults() != null) { +// for (Result result : queryResult.getResults()) { +// if (result.getSeries() != null) { +// for (Series serie : result.getSeries()) { +// if (serie.getValues() != null) { +// for (List l : serie.getValues()) { +// if (l.get(1) != null) { +// sumProduction += (Double) l.get(1) * second; +// } +// } +// } +// } +// } +// } +// } +// } catch (Exception e) { +// System.out.println("Error parsing LAST production: " + e); +// } +// +// Double avg = sumProduction / 3600 / 1000; +// +// JsonObject element = new JsonObject(); +// element.addProperty("value", avg); +// element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); +// jThing.add(productionChannels.get(i).toString(), element); +// } +// +// return jThing; +// } + +// private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { +// JsonArray gridThing = new JsonArray(); +// for (Entry entry : kWh.entrySet()) { +// String thingId = entry.getKey(); +// if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { +// gridThing.add(thingId); +// } +// } +// return gridThing; +// } +// +// private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { +// JsonArray storageThing = new JsonArray(); +// for (Entry entry : kWh.entrySet()) { +// String thingId = entry.getKey(); +// if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { +// storageThing.add(thingId); +// } +// } +// return storageThing; +// } +// +// private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) +// throws OpenemsException { +// ArrayList channelAddresses = new ArrayList<>(); +// for (Entry entry : channels.entrySet()) { +// String thingId = entry.getKey(); +// JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); +// for (JsonElement channelElement : channelIds) { +// String channelId = JsonUtils.getAsString(channelElement); +// if (channelId.contains("ActivePower")) { +// String name = thingId + "/" + channelId; +// boolean isGridOrStorage = false; +// for (int i = 0; i < things.size(); i++) { +// if (JsonUtils.getAsString(things.get(i)).equals(name)) { +// isGridOrStorage = true; +// } +// } +// if (!isGridOrStorage) { +// channelAddresses.add(thingId + "/" + channelId); +// } +// } +// } +// } +// return channelAddresses; +// } + + private static String toChannelAddressList(JsonObject channels) throws OpenemsException { + ArrayList channelAddresses = new ArrayList<>(); + for (Entry entry : channels.entrySet()) { + String thingId = entry.getKey(); + JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement channelElement : channelIds) { + String channelId = JsonUtils.getAsString(channelElement); + channelAddresses + .add("MEAN(\"" + thingId + "/" + channelId + "\") AS \"" + thingId + "/" + channelId + "\""); + } + } + return String.join(", ", channelAddresses); + } + + private static QueryResult executeQuery(InfluxDB influxdb, String database, String query) throws OpenemsException { + // Parse result + QueryResult queryResult; + try { + queryResult = influxdb.query(new Query(query, database), TimeUnit.MILLISECONDS); + } catch (RuntimeException e) { + throw new OpenemsException("InfluxDB query runtime error. Query: " + query + ", Error: " + e.getMessage()); + } + if (queryResult.hasError()) { + throw new OpenemsException("InfluxDB query error. Query: " + query + ", Error: " + queryResult.getError()); + } + return queryResult; + } +} diff --git a/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java b/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java index 2b8a50387e7..599bb62df7a 100644 --- a/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java +++ b/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java @@ -4,7 +4,7 @@ import org.junit.Test; -import io.openems.backend.timedata.influx.api.InfluxImpl; +import io.openems.backend.timedata.influx.InfluxProvider; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - InfluxImpl impl = new InfluxImpl(); + InfluxProvider impl = new InfluxProvider(); assertNotNull(impl); } diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd index 4dc256c37c6..20b8c70aadf 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -17,8 +17,8 @@ Export-Package: \ -buildpath: \ osgi.enroute.base.api;version=2.1,\ com.google.gson,\ - org.java-websocket:Java-WebSocket,\ - com.google.guava + com.google.guava,\ + io.openems.wrapper.websocket;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.common/src/io/openems/common/api/TimedataSource.java b/io.openems.common/src/io/openems/common/api/TimedataSource.java deleted file mode 100644 index 0744d3a81d4..00000000000 --- a/io.openems.common/src/io/openems/common/api/TimedataSource.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.openems.common.api; - -import java.time.ZonedDateTime; -import java.util.Optional; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; - -public interface TimedataSource { - /** - * Queries the database and returns a JsonArray of the form - * - *
    -	 *	[{
    -	 *  	timestamp: "2017-03-21T08:55:20Z",
    -	 *  	channels: {
    -	 *			'thing': {
    -	 *				'channel': 'value'
    -	 *			}
    -	 *		}
    -	 * 	}]
    -	 * 
    - * - * @param deviceId - * @param fromDate - * @param toDate - * @param channels - * @param resolution - * @return - * @throws OpenemsException - */ - public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, - int resolution/* , JsonObject kWh */) throws OpenemsException; -} diff --git a/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java index 7072b091f95..a15b206c92c 100644 --- a/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java +++ b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java @@ -1,9 +1,5 @@ package io.openems.common.websocket; -import java.time.Period; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; import java.util.Optional; import org.java_websocket.WebSocket; @@ -14,13 +10,10 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import io.openems.common.api.TimedataSource; -import io.openems.common.session.Role; -import io.openems.common.utils.JsonUtils; import io.openems.common.utils.StringUtils; public class WebSocketUtils { - + private static Logger log = LoggerFactory.getLogger(WebSocketUtils.class); public static boolean send(Optional websocketOpt, JsonObject j) { @@ -32,8 +25,8 @@ public static boolean send(Optional websocketOpt, JsonObject j) { } } - public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour, Notification code, - Object... params) { + public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour, + Notification code, Object... params) { if (!websocketOpt.isPresent()) { log.error("Websocket is not available. Unable to send Notification [" + String.format(code.getMessage(), params) + "]"); @@ -43,7 +36,8 @@ public static boolean sendNotification(Optional websocketOpt, JsonArr } } - public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour, Notification notification, Object... params) { + public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour, + Notification notification, Object... params) { if (logBehaviour.equals(LogBehaviour.WRITE_TO_LOG)) { // log message notification.writeToLog(log, params); @@ -69,45 +63,4 @@ public static boolean send(WebSocket websocket, JsonObject j) { return false; } } - - /** - * Query history command - * - * @param j - */ - public static JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, - TimedataSource timedataSource, Role role) { - try { - String mode = JsonUtils.getAsString(jHistoricData, "mode"); - if (mode.equals("query")) { - /* - * Query historic data - */ - int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); - ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); - ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); - ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); - JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); - // TODO check if role is allowed to read these channels - // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); - int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); - // TODO: better calculation of sensible resolution - int resolution = 10 * 60; // 10 Minutes - if (days > 25) { - resolution = 24 * 60 * 60; // 1 Day - } else if (days > 6) { - resolution = 3 * 60 * 60; // 3 Hours - } else if (days > 2) { - resolution = 60 * 60; // 60 Minutes - } - JsonArray jData = timedataSource.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); - // send reply - return DefaultMessages.historicDataQueryReply(jMessageId, jData); - } - } catch (Exception e) { - log.error("HistoricData Error: ", e); - e.printStackTrace(); - } - return new JsonObject(); - } } From d06a25a9aef42e5fd10b29d1cc7c0204842f33f3 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 31 Jan 2018 14:54:09 +0100 Subject: [PATCH 020/156] Work on logging --- cnf/central.xml | 30 ++++++++++ .../io.openems.backend.application.bndrun | 58 ++++++++++++++----- .../backend/application/BackendApp.java | 18 +++++- .../provider/BrowserWebsocketProvider.java | 1 + 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/cnf/central.xml b/cnf/central.xml index fb92f0ac46a..4cc7913e395 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -9,6 +9,16 @@ pom + + ch.qos.logback + logback-classic + 1.2.3 + + + ch.qos.logback + logback-core + 1.2.3 + com.google.code.gson @@ -61,6 +71,26 @@ org.apache.servicemix.bundles.ws-commons-util 1.0.2_2 + + org.slf4j + slf4j-api + 1.8.0-beta1 + + + org.slf4j + osgi-over-slf4j + 1.8.0-beta1 + + + org.ops4j.pax.logging + pax-logging-api + 1.8.3 + + + org.ops4j.pax.logging + pax-logging-service + 1.8.3 + diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 5b73302ec36..8a669c16fb8 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -8,7 +8,6 @@ Bundle-SymbolicName: io.openems.backend.application.launch JPM-Command: openems-backend -runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo)',\ osgi.identity;filter:='(osgi.identity=io.openems.common)',\ osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.commons-httpclient)',\ @@ -26,15 +25,19 @@ JPM-Command: openems-backend osgi.identity;filter:='(osgi.identity=io.openems.wrapper.websocket)',\ osgi.identity;filter:='(osgi.identity=org.apache.felix.configadmin)',\ osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.provider)',\ - osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' + osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)',\ + osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-service)' - - --resolve: auto +-runproperties: felix.cm.dir=C:/openems-config +#-runproperties: felix.cm.dir=/etc/openems.d +-runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' +-runee: JavaSE-1.8 -runbundles: \ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ + org.ops4j.pax.logging.pax-logging-service,\ com.google.gson;version='[2.8.2,2.8.3)',\ com.google.guava;version='[19.0.0,19.0.1)',\ - io.openems.backend.application;version=snapshot,\ io.openems.backend.browserwebsocket.api;version=snapshot,\ io.openems.backend.browserwebsocket.provider;version=snapshot,\ io.openems.backend.common;version=snapshot,\ @@ -49,10 +52,44 @@ JPM-Command: openems-backend org.apache.commons.codec;version='[1.10.0,1.10.1)',\ org.apache.commons.fileupload;version='[1.3.2,1.3.3)',\ org.apache.commons.io;version='[2.5.0,2.5.1)',\ - org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\ org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.apache.felix.webconsole;version='[4.2.16,4.2.17)',\ + org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ + org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ + org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ + org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ + org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.compendium;version='[4.1.0,4.1.1)',\ + org.osgi.service.event;version='[1.3.1,1.3.2)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + osgi.enroute.bostock.d3.webresource;version='[3.5.6,3.5.7)',\ + osgi.enroute.executor.simple.provider;version='[2.1.0,2.1.1)',\ + osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)',\ + osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.backend.browserwebsocket.api;version=snapshot,\ + io.openems.backend.browserwebsocket.provider;version=snapshot,\ + io.openems.backend.common;version=snapshot,\ + io.openems.backend.metadata.odoo;version=snapshot,\ + io.openems.backend.openemswebsocket.api;version=snapshot,\ + io.openems.backend.openemswebsocket.provider;version=snapshot,\ + io.openems.backend.timedata.api;version=snapshot,\ + io.openems.backend.timedata.influx;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + json;version='[20160212.0.0,20160212.0.1)',\ + org.apache.commons.codec;version='[1.10.0,1.10.1)',\ + org.apache.commons.fileupload;version='[1.3.2,1.3.3)',\ + org.apache.commons.io;version='[2.5.0,2.5.1)',\ + org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\ + org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.apache.felix.webconsole;version='[4.2.16,4.2.17)',\ org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ @@ -68,10 +105,5 @@ JPM-Command: openems-backend org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ osgi.enroute.bostock.d3.webresource;version='[3.5.6,3.5.7)',\ osgi.enroute.executor.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.logger.simple.provider;version='[2.1.0,2.1.1)',\ osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)' --runproperties: felix.cm.dir=C:/openems-config -#-runproperties: felix.cm.dir=/etc/openems.d --runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' --runee: JavaSE-1.8 \ No newline at end of file + osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)',\ \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 9dfffa7377a..8b4c7bdc3af 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -1,9 +1,16 @@ package io.openems.backend.application; +import java.io.IOException; +import java.util.Hashtable; + +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; +import org.osgi.service.log.LogService; +import org.slf4j.Logger; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.timedata.api.TimedataService; @@ -12,15 +19,22 @@ @Component() public class BackendApp { - // TODO: use setter to not kill BackendApp all the time... @Reference MetadataService metadataService; - + @Reference TimedataService timedataService; + @Reference + LogService log; + + @Reference + ConfigurationAdmin admin; + @Activate void activate() { + // log.info(arg0); + log.log(LogService.LOG_INFO, "Activate BackendAppX"); System.out.println("Activate BackendApp"); try { this.metadataService.getInfoWithSession("8635d53109cafc9d51de443c7d2bc4e980ba1b5d"); diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java index 7b7a4ae842c..01aeb9873b4 100644 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java @@ -12,6 +12,7 @@ import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.Log; import org.osgi.service.metatype.annotations.Designate; From 9462b8fa7a5cbc014546e2c992191fe8153e0691 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 1 Feb 2018 14:42:01 +0100 Subject: [PATCH 021/156] Enable logging using Pax --- io.openems.backend.application/bnd.bnd | 4 +- .../io.openems.backend.application.bndrun | 99 ++----------------- .../backend/application/BackendApp.java | 42 ++++---- .../org.eclipse.core.resources.prefs | 1 - .../bnd.bnd | 2 - .../readme.md | 8 -- .../bnd.bnd | 6 +- ...s.backend.browserwebsocket.provider.bndrun | 24 ++++- .../provider/BrowserWebsocketProvider.java | 5 + 9 files changed, 66 insertions(+), 125 deletions(-) delete mode 100644 io.openems.backend.browserwebsocket.api/readme.md diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index 1350996f7d1..75821e5f1b1 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -14,7 +14,9 @@ Private-Package: \ osgi.enroute.base.api;version=2.1,\ io.openems.backend.metadata.api;version=latest,\ io.openems.common;version=latest,\ - io.openems.backend.timedata.api;version=latest + io.openems.backend.timedata.api;version=latest,\ + io.openems.backend.browserwebsocket.api;version=latest,\ + io.openems.backend.openemswebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 8a669c16fb8..42576694faa 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -8,102 +8,23 @@ Bundle-SymbolicName: io.openems.backend.application.launch JPM-Command: openems-backend -runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo)',\ - osgi.identity;filter:='(osgi.identity=io.openems.common)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.commons-httpclient)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.ws-commons-util)',\ - osgi.identity;filter:='(osgi.identity=com.google.gson)',\ - osgi.identity;filter:='(osgi.identity=org.apache.felix.webconsole)',\ - osgi.identity;filter:='(osgi.identity=osgi.enroute.webconsole.xray.provider)',\ - osgi.identity;filter:='(osgi.identity=com.google.guava)',\ - osgi.identity;filter:='(osgi.identity=io.openems.backend.common)',\ - osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.influxdb-java)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.okhttp)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.okio)',\ - osgi.identity;filter:='(osgi.identity=org.apache.servicemix.bundles.retrofit)',\ - osgi.identity;filter:='(osgi.identity=io.openems.wrapper.websocket)',\ - osgi.identity;filter:='(osgi.identity=org.apache.felix.configadmin)',\ - osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.provider)',\ - osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)',\ - osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-service)' + osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ + osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-service)',\ + osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-api)' --runproperties: felix.cm.dir=C:/openems-config +-runproperties: \ + felix.cm.dir=C:/openems-config,\ + org.ops4j.pax.logging.service.frameworkEventsLogLevel="DISABLED" #-runproperties: felix.cm.dir=/etc/openems.d -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' -runee: JavaSE-1.8 -runbundles: \ - org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ - org.ops4j.pax.logging.pax-logging-service,\ - com.google.gson;version='[2.8.2,2.8.3)',\ - com.google.guava;version='[19.0.0,19.0.1)',\ - io.openems.backend.browserwebsocket.api;version=snapshot,\ - io.openems.backend.browserwebsocket.provider;version=snapshot,\ - io.openems.backend.common;version=snapshot,\ - io.openems.backend.metadata.odoo;version=snapshot,\ - io.openems.backend.openemswebsocket.api;version=snapshot,\ - io.openems.backend.openemswebsocket.provider;version=snapshot,\ - io.openems.backend.timedata.api;version=snapshot,\ - io.openems.backend.timedata.influx;version=snapshot,\ - io.openems.common;version=snapshot,\ - io.openems.wrapper.websocket;version=snapshot,\ - json;version='[20160212.0.0,20160212.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ - org.apache.commons.fileupload;version='[1.3.2,1.3.3)',\ - org.apache.commons.io;version='[2.5.0,2.5.1)',\ - org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ - org.apache.felix.scr;version='[2.0.2,2.0.3)',\ - org.apache.felix.webconsole;version='[4.2.16,4.2.17)',\ - org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ - org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ - org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ - org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ - org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ - org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ - org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ - org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ - org.osgi.compendium;version='[4.1.0,4.1.1)',\ - org.osgi.service.event;version='[1.3.1,1.3.2)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - osgi.enroute.bostock.d3.webresource;version='[3.5.6,3.5.7)',\ - osgi.enroute.executor.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)',\ + io.openems.backend.application;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ - com.google.gson;version='[2.8.2,2.8.3)',\ - com.google.guava;version='[19.0.0,19.0.1)',\ - io.openems.backend.browserwebsocket.api;version=snapshot,\ - io.openems.backend.browserwebsocket.provider;version=snapshot,\ - io.openems.backend.common;version=snapshot,\ - io.openems.backend.metadata.odoo;version=snapshot,\ - io.openems.backend.openemswebsocket.api;version=snapshot,\ - io.openems.backend.openemswebsocket.provider;version=snapshot,\ - io.openems.backend.timedata.api;version=snapshot,\ - io.openems.backend.timedata.influx;version=snapshot,\ - io.openems.common;version=snapshot,\ - io.openems.wrapper.websocket;version=snapshot,\ - json;version='[20160212.0.0,20160212.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ - org.apache.commons.fileupload;version='[1.3.2,1.3.3)',\ - org.apache.commons.io;version='[2.5.0,2.5.1)',\ - org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\ - org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ - org.apache.felix.webconsole;version='[4.2.16,4.2.17)',\ - org.apache.servicemix.bundles.commons-httpclient;version='[3.1.0,3.1.1)',\ - org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ - org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ - org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ - org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ - org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ - org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ - org.osgi.compendium;version='[4.1.0,4.1.1)',\ - org.osgi.service.event;version='[1.3.1,1.3.2)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - osgi.enroute.bostock.d3.webresource;version='[3.5.6,3.5.7)',\ - osgi.enroute.executor.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)',\ - osgi.enroute.webconsole.xray.provider;version='[2.1.0,2.1.1)',\ \ No newline at end of file + org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)' +-resolve: auto diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 8b4c7bdc3af..bf74f543d7f 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -9,43 +9,43 @@ import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.log.LogService; import org.slf4j.Logger; - -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.timedata.api.TimedataService; -import io.openems.common.exceptions.OpenemsException; +import org.slf4j.LoggerFactory; @Component() public class BackendApp { - @Reference - MetadataService metadataService; + private final Logger log = LoggerFactory.getLogger(BackendApp.class); @Reference - TimedataService timedataService; - - @Reference - LogService log; - - @Reference - ConfigurationAdmin admin; + ConfigurationAdmin configAdmin; @Activate void activate() { - // log.info(arg0); - log.log(LogService.LOG_INFO, "Activate BackendAppX"); - System.out.println("Activate BackendApp"); + configureLogging(); + + log.debug("Activate BackendApp"); + } + + private void configureLogging() { + Configuration configuration; try { - this.metadataService.getInfoWithSession("8635d53109cafc9d51de443c7d2bc4e980ba1b5d"); - } catch (OpenemsException e) { - e.printStackTrace(); + configuration = configAdmin.getConfiguration("org.ops4j.pax.logging", null); + final Hashtable log4jProps = new Hashtable(); + log4jProps.put("log4j.rootLogger", "DEBUG, CONSOLE"); + log4jProps.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); + log4jProps.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); + log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); + log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); + configuration.update(log4jProps); + } catch (IOException e1) { + e1.printStackTrace(); } } @Deactivate void deactivate() { - System.out.println("Deactivate"); + log.debug("Deactivate BackendApp"); } } diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs index 26e809422ae..1addf186947 100644 --- a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs @@ -3,4 +3,3 @@ encoding//src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.ja encoding//src/io/openems/backend/browserwebsocket/api/package-info.java=UTF-8 encoding//test/.gitignore=UTF-8 encoding/bnd.bnd=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/bnd.bnd b/io.openems.backend.browserwebsocket.api/bnd.bnd index f12cf5d3f22..a6479c339c3 100644 --- a/io.openems.backend.browserwebsocket.api/bnd.bnd +++ b/io.openems.backend.browserwebsocket.api/bnd.bnd @@ -7,8 +7,6 @@ Bundle-Version: 1.0.0.${tstamp} Export-Package: \ io.openems.backend.browserwebsocket.api --includeresource: {readme.md} - -buildpath: \ osgi.enroute.base.api;version=2.1,\ io.openems.wrapper.websocket;version=latest diff --git a/io.openems.backend.browserwebsocket.api/readme.md b/io.openems.backend.browserwebsocket.api/readme.md deleted file mode 100644 index 109f3501a3b..00000000000 --- a/io.openems.backend.browserwebsocket.api/readme.md +++ /dev/null @@ -1,8 +0,0 @@ -# io.openems.backend.browserwebsocket.api - -${Bundle-Description} - -## Example - -## References - diff --git a/io.openems.backend.browserwebsocket.provider/bnd.bnd b/io.openems.backend.browserwebsocket.provider/bnd.bnd index 1ff0f8672cd..0228b34f6c4 100644 --- a/io.openems.backend.browserwebsocket.provider/bnd.bnd +++ b/io.openems.backend.browserwebsocket.provider/bnd.bnd @@ -4,9 +4,10 @@ Bundle-Version: 1.0.0.${tstamp} -Export-Package: io.openems.backend.browserwebsocket.provider -Private-Package: io.openems.backend.browserwebsocket.provider.internal +Private-Package: \ + io.openems.backend.browserwebsocket.provider.internal,\ + io.openems.backend.browserwebsocket.provider -includeresource: {readme.md} @@ -25,3 +26,4 @@ Private-Package: io.openems.backend.browserwebsocket.provider.internal osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' \ No newline at end of file diff --git a/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun b/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun index e96468a57b8..33edb84cd55 100644 --- a/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun +++ b/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun @@ -11,4 +11,26 @@ JPM-Command: provider -runrequires: \ osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' --runbundles: ${error;You must first resolve this bndrun file before you can run it} +-runbundles: \ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.backend.browserwebsocket.api;version=snapshot,\ + io.openems.backend.browserwebsocket.provider;version=snapshot,\ + io.openems.backend.metadata.api;version=snapshot,\ + io.openems.backend.metadata.odoo;version=snapshot,\ + io.openems.backend.openemswebsocket.api;version=snapshot,\ + io.openems.backend.openemswebsocket.provider;version=snapshot,\ + io.openems.backend.timedata.api;version=snapshot,\ + io.openems.backend.timedata.influx;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ + org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ + org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + slf4j.api;version='[1.8.0,1.8.1)' diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java index 01aeb9873b4..088388fae59 100644 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java +++ b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java @@ -5,7 +5,9 @@ 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.log.LogService; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; import io.openems.backend.browserwebsocket.provider.internal.BrowserWebsocket; import io.openems.backend.metadata.api.MetadataService; @@ -20,6 +22,9 @@ @Component(name = "BrowserWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE) public class BrowserWebsocketProvider { + @Reference + Logger log; + @ObjectClassDefinition @interface Config { int port(); From 1eeb2e2c784cb9ebd24b4d160d2285abe60a61cc Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 1 Feb 2018 15:18:12 +0100 Subject: [PATCH 022/156] Rename package to odoo.provider. Enable startup of odoo bundle --- .../io.openems.backend.application.bndrun | 7 ++++++- .../io/openems/backend/application/BackendApp.java | 7 +++++++ io.openems.backend.metadata.api/bnd.bnd | 3 +++ .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../.settings/org.eclipse.core.resources.prefs | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 0 .../debug.bndrun | 0 .../io.openems.backend.metadata.odoo.bndrun | 0 .../readme.md | 0 .../src/io/openems/backend/metadata/odoo/Odoo.java | 12 ++++++++---- .../openems/backend/metadata/odoo/package-info.java | 0 .../backend/metadata/odoo/ProviderImplTest.java | 4 ++-- 15 files changed, 28 insertions(+), 9 deletions(-) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/.classpath (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/.gitignore (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/.project (89%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/.settings/org.eclipse.core.resources.prefs (76%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/bnd.bnd (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/debug.bndrun (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/io.openems.backend.metadata.odoo.bndrun (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/readme.md (100%) rename io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java => io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java (94%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/src/io/openems/backend/metadata/odoo/package-info.java (100%) rename {io.openems.backend.metadata.odoo => io.openems.backend.metadata.odoo.provider}/test/io/openems/backend/metadata/odoo/ProviderImplTest.java (74%) diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 42576694faa..91d963414c4 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -26,5 +26,10 @@ JPM-Command: openems-backend org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)' + org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)',\ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + io.openems.backend.metadata.odoo.provider;version=snapshot -resolve: auto diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index bf74f543d7f..00b7d756b53 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -12,6 +12,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.backend.metadata.api.MetadataService; + @Component() public class BackendApp { @@ -20,6 +22,9 @@ public class BackendApp { @Reference ConfigurationAdmin configAdmin; + @Reference + MetadataService metadataService; + @Activate void activate() { configureLogging(); @@ -36,7 +41,9 @@ private void configureLogging() { log4jProps.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); log4jProps.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); + // set minimum log levels for some verbose packages log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); + log4jProps.put("log4j.logger.org.apache.felix.configadmin", "INFO"); configuration.update(log4jProps); } catch (IOException e1) { e1.printStackTrace(); diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd index 6fb257cb03c..64db203fafd 100644 --- a/io.openems.backend.metadata.api/bnd.bnd +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -8,6 +8,9 @@ Bundle-Description: Read and store Metadata Export-Package: \ io.openems.backend.metadata.api +Require-Capability: \ + compile-only + -includeresource: {readme.md} -buildpath: \ diff --git a/io.openems.backend.metadata.odoo/.classpath b/io.openems.backend.metadata.odoo.provider/.classpath similarity index 100% rename from io.openems.backend.metadata.odoo/.classpath rename to io.openems.backend.metadata.odoo.provider/.classpath diff --git a/io.openems.backend.metadata.odoo/.gitignore b/io.openems.backend.metadata.odoo.provider/.gitignore similarity index 100% rename from io.openems.backend.metadata.odoo/.gitignore rename to io.openems.backend.metadata.odoo.provider/.gitignore diff --git a/io.openems.backend.metadata.odoo/.project b/io.openems.backend.metadata.odoo.provider/.project similarity index 89% rename from io.openems.backend.metadata.odoo/.project rename to io.openems.backend.metadata.odoo.provider/.project index 57448a32928..329727bcb13 100644 --- a/io.openems.backend.metadata.odoo/.project +++ b/io.openems.backend.metadata.odoo.provider/.project @@ -1,6 +1,6 @@ - io.openems.backend.metadata.odoo + io.openems.backend.metadata.odoo.provider diff --git a/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.odoo.provider/.settings/org.eclipse.core.resources.prefs similarity index 76% rename from io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs rename to io.openems.backend.metadata.odoo.provider/.settings/org.eclipse.core.resources.prefs index 613b3f9d649..650a9b636d3 100644 --- a/io.openems.backend.metadata.odoo/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.metadata.odoo.provider/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,5 @@ eclipse.preferences.version=1 -encoding//src/io/openems/backend/metadata/odoo/OdooProvider.java=UTF-8 +encoding//src/io/openems/backend/metadata/odoo/Odoo.java=UTF-8 encoding//test/io/openems/backend/metadata/odoo/ProviderImplTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/debug.bndrun=UTF-8 diff --git a/io.openems.backend.metadata.odoo/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.metadata.odoo.provider/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.metadata.odoo/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.metadata.odoo.provider/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.metadata.odoo/bnd.bnd b/io.openems.backend.metadata.odoo.provider/bnd.bnd similarity index 100% rename from io.openems.backend.metadata.odoo/bnd.bnd rename to io.openems.backend.metadata.odoo.provider/bnd.bnd diff --git a/io.openems.backend.metadata.odoo/debug.bndrun b/io.openems.backend.metadata.odoo.provider/debug.bndrun similarity index 100% rename from io.openems.backend.metadata.odoo/debug.bndrun rename to io.openems.backend.metadata.odoo.provider/debug.bndrun diff --git a/io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun b/io.openems.backend.metadata.odoo.provider/io.openems.backend.metadata.odoo.bndrun similarity index 100% rename from io.openems.backend.metadata.odoo/io.openems.backend.metadata.odoo.bndrun rename to io.openems.backend.metadata.odoo.provider/io.openems.backend.metadata.odoo.bndrun diff --git a/io.openems.backend.metadata.odoo/readme.md b/io.openems.backend.metadata.odoo.provider/readme.md similarity index 100% rename from io.openems.backend.metadata.odoo/readme.md rename to io.openems.backend.metadata.odoo.provider/readme.md diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java similarity index 94% rename from io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java rename to io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 34e99e5cf2d..ed4b83cc7c5 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooProvider.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -6,6 +6,8 @@ import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.openems.backend.metadata.api.MetadataDeviceModel; import io.openems.backend.metadata.api.MetadataService; @@ -29,10 +31,12 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -@Designate(ocd = OdooProvider.Config.class, factory = false) +@Designate(ocd = Odoo.Config.class, factory = false) @Component(name = "Odoo", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class OdooProvider implements MetadataService { +public class Odoo implements MetadataService { + private final Logger log = LoggerFactory.getLogger(Odoo.class); + @ObjectClassDefinition @interface Config { String database(); @@ -51,7 +55,7 @@ public class OdooProvider implements MetadataService { @Activate void activate(Config config) { - System.out.println("Activate Odoo"); + log.debug("Activate Odoo"); this.url = config.url(); this.database = config.database(); this.uid = config.uid(); @@ -60,7 +64,7 @@ void activate(Config config) { @Deactivate void deactivate() { - System.out.println("Deactivate Odoo"); + log.debug("Deactivate Odoo"); } private MetadataDeviceModel deviceModel; diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/package-info.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/package-info.java similarity index 100% rename from io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/package-info.java rename to io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/package-info.java diff --git a/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java b/io.openems.backend.metadata.odoo.provider/test/io/openems/backend/metadata/odoo/ProviderImplTest.java similarity index 74% rename from io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java rename to io.openems.backend.metadata.odoo.provider/test/io/openems/backend/metadata/odoo/ProviderImplTest.java index 7553f3e7491..efb85bb1b0a 100644 --- a/io.openems.backend.metadata.odoo/test/io/openems/backend/metadata/odoo/ProviderImplTest.java +++ b/io.openems.backend.metadata.odoo.provider/test/io/openems/backend/metadata/odoo/ProviderImplTest.java @@ -4,7 +4,7 @@ import org.junit.Test; -import io.openems.backend.metadata.odoo.OdooProvider; +import io.openems.backend.metadata.odoo.Odoo; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - OdooProvider impl = new OdooProvider(); + Odoo impl = new Odoo(); assertNotNull(impl); } From 9e9b022f333ba2b5d10d1872ce5dea3deb00ec96 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 1 Feb 2018 15:23:30 +0100 Subject: [PATCH 023/156] Rename package to influx.provider. Enable startup of influx bundle --- .../io.openems.backend.application.bndrun | 7 ++++++- .../io/openems/backend/application/BackendApp.java | 4 ++++ io.openems.backend.timedata.api/bnd.bnd | 3 +++ .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../.settings/org.eclipse.core.resources.prefs | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 5 +++-- .../debug.bndrun | 0 .../io.openems.backend.timedata.influx.bndrun | 0 .../readme.md | 0 .../backend/timedata/influx}/DeviceCache.java | 2 +- .../io/openems/backend/timedata/influx/Influx.java | 14 ++++++++------ .../backend/timedata/influx}/InfluxdbUtils.java | 2 +- .../backend/timedata/influx/package-info.java | 2 ++ .../backend/timedata/influx/ProviderImplTest.java | 4 ++-- 17 files changed, 32 insertions(+), 15 deletions(-) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/.classpath (100%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/.gitignore (100%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/.project (89%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/.settings/org.eclipse.core.resources.prefs (76%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/bnd.bnd (81%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/debug.bndrun (100%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/io.openems.backend.timedata.influx.bndrun (100%) rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/readme.md (100%) rename {io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal => io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx}/DeviceCache.java (94%) rename io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java => io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java (97%) rename {io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal => io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx}/InfluxdbUtils.java (99%) create mode 100644 io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/package-info.java rename {io.openems.backend.timedata.influx => io.openems.backend.timedata.influx.provider}/test/io/openems/backend/timedata/influx/ProviderImplTest.java (73%) diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 91d963414c4..e44fac82c25 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -31,5 +31,10 @@ JPM-Command: openems-backend com.google.guava;version='[19.0.0,19.0.1)',\ io.openems.common;version=snapshot,\ io.openems.wrapper.websocket;version=snapshot,\ - io.openems.backend.metadata.odoo.provider;version=snapshot + io.openems.backend.metadata.odoo.provider;version=snapshot,\ + io.openems.backend.timedata.influx.provider;version=snapshot,\ + org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ + org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ + org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)' -resolve: auto diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 00b7d756b53..f704daf01ab 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.timedata.api.TimedataService; @Component() public class BackendApp { @@ -25,6 +26,9 @@ public class BackendApp { @Reference MetadataService metadataService; + @Reference + TimedataService timedataService; + @Activate void activate() { configureLogging(); diff --git a/io.openems.backend.timedata.api/bnd.bnd b/io.openems.backend.timedata.api/bnd.bnd index 0090555f31f..eb32c7e5dec 100644 --- a/io.openems.backend.timedata.api/bnd.bnd +++ b/io.openems.backend.timedata.api/bnd.bnd @@ -6,6 +6,9 @@ Bundle-Version: 1.0.0.${tstamp} Export-Package: io.openems.backend.timedata.api +Require-Capability: \ + compile-only + -includeresource: {readme.md} -buildpath: \ diff --git a/io.openems.backend.timedata.influx/.classpath b/io.openems.backend.timedata.influx.provider/.classpath similarity index 100% rename from io.openems.backend.timedata.influx/.classpath rename to io.openems.backend.timedata.influx.provider/.classpath diff --git a/io.openems.backend.timedata.influx/.gitignore b/io.openems.backend.timedata.influx.provider/.gitignore similarity index 100% rename from io.openems.backend.timedata.influx/.gitignore rename to io.openems.backend.timedata.influx.provider/.gitignore diff --git a/io.openems.backend.timedata.influx/.project b/io.openems.backend.timedata.influx.provider/.project similarity index 89% rename from io.openems.backend.timedata.influx/.project rename to io.openems.backend.timedata.influx.provider/.project index 209c2129b0a..450968a4f97 100644 --- a/io.openems.backend.timedata.influx/.project +++ b/io.openems.backend.timedata.influx.provider/.project @@ -1,6 +1,6 @@ - io.openems.backend.timedata.influx + io.openems.backend.timedata.influx.provider diff --git a/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata.influx.provider/.settings/org.eclipse.core.resources.prefs similarity index 76% rename from io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs rename to io.openems.backend.timedata.influx.provider/.settings/org.eclipse.core.resources.prefs index 6e0123932e9..743b8c6e378 100644 --- a/io.openems.backend.timedata.influx/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.timedata.influx.provider/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,5 @@ eclipse.preferences.version=1 -encoding//src/io/openems/backend/timedata/influx/InfluxProvider.java=UTF-8 +encoding//src/io/openems/backend/timedata/influx/Influx.java=UTF-8 encoding//test/io/openems/backend/timedata/influx/ProviderImplTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/debug.bndrun=UTF-8 diff --git a/io.openems.backend.timedata.influx/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.timedata.influx.provider/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.timedata.influx/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.timedata.influx.provider/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.timedata.influx/bnd.bnd b/io.openems.backend.timedata.influx.provider/bnd.bnd similarity index 81% rename from io.openems.backend.timedata.influx/bnd.bnd rename to io.openems.backend.timedata.influx.provider/bnd.bnd index f8b5e96718c..a511cc64f61 100644 --- a/io.openems.backend.timedata.influx/bnd.bnd +++ b/io.openems.backend.timedata.influx.provider/bnd.bnd @@ -4,9 +4,9 @@ Bundle-Version: 1.0.0.${tstamp} -Export-Package: io.openems.backend.timedata.influx;-provide=true -Private-Package: io.openems.backend.timedata.influx.internal +Private-Package: \ + io.openems.backend.timedata.influx -includeresource: {readme.md} @@ -24,3 +24,4 @@ Private-Package: io.openems.backend.timedata.influx.internal osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 +Export-Package: io.openems.backend.timedata.api \ No newline at end of file diff --git a/io.openems.backend.timedata.influx/debug.bndrun b/io.openems.backend.timedata.influx.provider/debug.bndrun similarity index 100% rename from io.openems.backend.timedata.influx/debug.bndrun rename to io.openems.backend.timedata.influx.provider/debug.bndrun diff --git a/io.openems.backend.timedata.influx/io.openems.backend.timedata.influx.bndrun b/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun similarity index 100% rename from io.openems.backend.timedata.influx/io.openems.backend.timedata.influx.bndrun rename to io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun diff --git a/io.openems.backend.timedata.influx/readme.md b/io.openems.backend.timedata.influx.provider/readme.md similarity index 100% rename from io.openems.backend.timedata.influx/readme.md rename to io.openems.backend.timedata.influx.provider/readme.md diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/DeviceCache.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/DeviceCache.java similarity index 94% rename from io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/DeviceCache.java rename to io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/DeviceCache.java index 60ca0d9218c..4c9c565d79b 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/DeviceCache.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/DeviceCache.java @@ -1,4 +1,4 @@ -package io.openems.backend.timedata.influx.internal; +package io.openems.backend.timedata.influx; import java.util.HashMap; import java.util.Map; diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java similarity index 97% rename from io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java rename to io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index 1edbb33827d..07346ec00aa 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/InfluxProvider.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -18,6 +18,8 @@ import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.collect.TreeBasedTable; import com.google.gson.JsonArray; @@ -27,18 +29,18 @@ import io.openems.backend.metadata.api.MetadataDevice; import io.openems.backend.metadata.api.MetadataDevices; import io.openems.backend.timedata.api.TimedataService; -import io.openems.backend.timedata.influx.internal.DeviceCache; -import io.openems.backend.timedata.influx.internal.InfluxdbUtils; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; import org.osgi.service.metatype.annotations.Designate; -@Designate(ocd = InfluxProvider.Config.class, factory = false) +@Designate(ocd = Influx.Config.class, factory = false) @Component(name = "InfluxDB", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class InfluxProvider implements TimedataService { +public class Influx implements TimedataService { + private final Logger log = LoggerFactory.getLogger(Influx.class); + private final String TMP_MINI_MEASUREMENT = "minies"; @ObjectClassDefinition @@ -69,7 +71,7 @@ public class InfluxProvider implements TimedataService { @Activate void activate(Config config) throws OpenemsException { - System.out.println("Activate InfluxDB"); + log.debug("Activate InfluxDB"); this.database = config.database(); this.url = config.url(); this.port = config.port(); @@ -87,7 +89,7 @@ void activate(Config config) throws OpenemsException { @Deactivate void deactivate() { - System.out.println("Deactivate InfluxDB"); + log.debug("Deactivate InfluxDB"); } private void connect() throws Exception { diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/InfluxdbUtils.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java similarity index 99% rename from io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/InfluxdbUtils.java rename to io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java index 7a881b121bd..358b1082633 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/internal/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java @@ -1,4 +1,4 @@ -package io.openems.backend.timedata.influx.internal; +package io.openems.backend.timedata.influx; import java.time.Instant; import java.time.ZonedDateTime; diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/package-info.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/package-info.java new file mode 100644 index 00000000000..43990b5a888 --- /dev/null +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.timedata.influx; diff --git a/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java b/io.openems.backend.timedata.influx.provider/test/io/openems/backend/timedata/influx/ProviderImplTest.java similarity index 73% rename from io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java rename to io.openems.backend.timedata.influx.provider/test/io/openems/backend/timedata/influx/ProviderImplTest.java index 599bb62df7a..fb887cf95af 100644 --- a/io.openems.backend.timedata.influx/test/io/openems/backend/timedata/influx/ProviderImplTest.java +++ b/io.openems.backend.timedata.influx.provider/test/io/openems/backend/timedata/influx/ProviderImplTest.java @@ -4,7 +4,7 @@ import org.junit.Test; -import io.openems.backend.timedata.influx.InfluxProvider; +import io.openems.backend.timedata.influx.Influx; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - InfluxProvider impl = new InfluxProvider(); + Influx impl = new Influx(); assertNotNull(impl); } From b894d0936d55666865b581b5105ef6694c517a4b Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 1 Feb 2018 16:20:05 +0100 Subject: [PATCH 024/156] Rename package to openemswebsocket.impl.provider. Enable startup of openemswebsocket bundle --- io.openems.backend.application/bnd.bnd | 1 - .../io.openems.backend.application.bndrun | 25 +- .../backend/application/BackendApp.java | 4 + .../org.eclipse.core.resources.prefs | 5 - .../bnd.bnd | 17 - .../api/BrowserWebsocketService.java | 21 - .../browserwebsocket/api/package-info.java | 2 - .../test/.gitignore | 0 .../.classpath | 8 - .../.gitignore | 3 - .../.project | 23 - .../org.eclipse.core.resources.prefs | 7 - .../.settings/org.eclipse.jdt.core.prefs | 11 - .../bnd.bnd | 29 -- .../debug.bndrun | 13 - ...s.backend.browserwebsocket.provider.bndrun | 36 -- .../readme.md | 8 - .../provider/BrowserWebsocketProvider.java | 65 --- .../internal/BackendCurrentDataWorker.java | 53 --- .../provider/internal/BrowserSession.java | 15 - .../provider/internal/BrowserSessionData.java | 70 --- .../internal/BrowserSessionManager.java | 11 - .../provider/internal/BrowserWebsocket.java | 441 ------------------ .../provider/package-info.java | 2 - .../impl/ProviderImplTest.java | 26 -- .../bnd.bnd | 2 +- .../bnd.bnd | 13 +- .../api/OpenemsWebsocketService.java | 12 - .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../org.eclipse.core.resources.prefs | 8 + .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 21 + .../debug.bndrun | 13 + ...s.backend.openemswebsocket.provider.bndrun | 20 + .../readme.md | 8 + .../impl/provider/OpenemsWebsocket.java | 35 ++ .../impl/provider/package-info.java | 2 + .../provider}/ProviderImplTest.java | 6 +- .../.classpath | 8 - .../.gitignore | 3 - .../.project | 23 - .../org.eclipse.core.resources.prefs | 7 - .../.settings/org.eclipse.jdt.core.prefs | 11 - .../bnd.bnd | 28 -- .../debug.bndrun | 13 - ....openemswebsocket.provider.bndrun.resolved | 7 + ...enems.backend.openemswebsocket.impl.bndrun | 14 - .../readme.md | 8 - .../provider/OpenemsWebsocketProvider.java | 93 ---- .../provider/internal/OpenemsSession.java | 15 - .../provider/internal/OpenemsSessionData.java | 44 -- .../internal/OpenemsSessionManager.java | 54 --- .../provider/internal/OpenemsWebsocket.java | 347 -------------- .../provider/package-info.java | 2 - .../bnd.bnd | 2 +- 57 files changed, 146 insertions(+), 1571 deletions(-) delete mode 100644 io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.browserwebsocket.api/bnd.bnd delete mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java delete mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java delete mode 100644 io.openems.backend.browserwebsocket.api/test/.gitignore delete mode 100644 io.openems.backend.browserwebsocket.provider/.classpath delete mode 100644 io.openems.backend.browserwebsocket.provider/.gitignore delete mode 100644 io.openems.backend.browserwebsocket.provider/.project delete mode 100644 io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.jdt.core.prefs delete mode 100644 io.openems.backend.browserwebsocket.provider/bnd.bnd delete mode 100644 io.openems.backend.browserwebsocket.provider/debug.bndrun delete mode 100644 io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun delete mode 100644 io.openems.backend.browserwebsocket.provider/readme.md delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java delete mode 100644 io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java delete mode 100644 io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java rename {io.openems.backend.browserwebsocket.api => io.openems.backend.openemswebsocket.impl.provider}/.classpath (100%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.openemswebsocket.impl.provider}/.gitignore (100%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.openemswebsocket.impl.provider}/.project (88%) create mode 100644 io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs rename {io.openems.backend.browserwebsocket.api => io.openems.backend.openemswebsocket.impl.provider}/.settings/org.eclipse.jdt.core.prefs (100%) create mode 100644 io.openems.backend.openemswebsocket.impl.provider/bnd.bnd create mode 100644 io.openems.backend.openemswebsocket.impl.provider/debug.bndrun create mode 100644 io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun create mode 100644 io.openems.backend.openemswebsocket.impl.provider/readme.md create mode 100644 io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java create mode 100644 io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java rename {io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl => io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider}/ProviderImplTest.java (55%) delete mode 100644 io.openems.backend.openemswebsocket.provider/.classpath delete mode 100644 io.openems.backend.openemswebsocket.provider/.gitignore delete mode 100644 io.openems.backend.openemswebsocket.provider/.project delete mode 100644 io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs delete mode 100644 io.openems.backend.openemswebsocket.provider/bnd.bnd delete mode 100644 io.openems.backend.openemswebsocket.provider/debug.bndrun create mode 100644 io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved delete mode 100644 io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun delete mode 100644 io.openems.backend.openemswebsocket.provider/readme.md delete mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java delete mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java delete mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java delete mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java delete mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java delete mode 100644 io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index 75821e5f1b1..3006e386f94 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -15,7 +15,6 @@ Private-Package: \ io.openems.backend.metadata.api;version=latest,\ io.openems.common;version=latest,\ io.openems.backend.timedata.api;version=latest,\ - io.openems.backend.browserwebsocket.api;version=latest,\ io.openems.backend.openemswebsocket.api;version=latest -testpath: \ diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index e44fac82c25..05ec3444b0c 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -18,23 +18,24 @@ JPM-Command: openems-backend #-runproperties: felix.cm.dir=/etc/openems.d -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' -runee: JavaSE-1.8 +-resolve: auto -runbundles: \ - org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ - io.openems.backend.application;version=snapshot,\ - org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ - org.apache.felix.log;version='[1.0.1,1.0.2)',\ - org.apache.felix.scr;version='[2.0.2,2.0.3)',\ - org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)',\ com.google.gson;version='[2.8.2,2.8.3)',\ com.google.guava;version='[19.0.0,19.0.1)',\ - io.openems.common;version=snapshot,\ - io.openems.wrapper.websocket;version=snapshot,\ + io.openems.backend.application;version=snapshot,\ io.openems.backend.metadata.odoo.provider;version=snapshot,\ io.openems.backend.timedata.influx.provider;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ - org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)' --resolve: auto + org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ + org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + io.openems.backend.openemswebsocket.impl.provider;version=snapshot \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index f704daf01ab..f65daffaf92 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; import io.openems.backend.timedata.api.TimedataService; @Component() @@ -29,6 +30,9 @@ public class BackendApp { @Reference TimedataService timedataService; + @Reference + OpenemsWebsocketService openemsWebsocketService; + @Activate void activate() { configureLogging(); diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 1addf186947..00000000000 --- a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java=UTF-8 -encoding//src/io/openems/backend/browserwebsocket/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/bnd.bnd b/io.openems.backend.browserwebsocket.api/bnd.bnd deleted file mode 100644 index a6479c339c3..00000000000 --- a/io.openems.backend.browserwebsocket.api/bnd.bnd +++ /dev/null @@ -1,17 +0,0 @@ -# -# io.openems.backend.browserwebsocket.api DEFAULTS -# - -Bundle-Version: 1.0.0.${tstamp} - -Export-Package: \ - io.openems.backend.browserwebsocket.api - --buildpath: \ - osgi.enroute.base.api;version=2.1,\ - io.openems.wrapper.websocket;version=latest - --testpath: \ - osgi.enroute.junit.wrapper;version=4.12, \ - osgi.enroute.hamcrest.wrapper;version=1.3 - diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java deleted file mode 100644 index a3859524110..00000000000 --- a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.openems.backend.browserwebsocket.api; - -import java.util.Optional; -import java.util.Set; - -import org.java_websocket.WebSocket; -import org.osgi.annotation.versioning.ProviderType; - -@ProviderType -public interface BrowserWebsocketService { - - /** - * Announce browserWebsocket that this OpenEMS Edge was connected - * - * @param deviceNames - */ - void openemsConnectionOpened(Set deviceNames); - - Optional getWebsocketByToken(String token); - -} diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java deleted file mode 100644 index b1bf5e6e5e2..00000000000 --- a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.browserwebsocket.api; diff --git a/io.openems.backend.browserwebsocket.api/test/.gitignore b/io.openems.backend.browserwebsocket.api/test/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/io.openems.backend.browserwebsocket.provider/.classpath b/io.openems.backend.browserwebsocket.provider/.classpath deleted file mode 100644 index 26009f42341..00000000000 --- a/io.openems.backend.browserwebsocket.provider/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/io.openems.backend.browserwebsocket.provider/.gitignore b/io.openems.backend.browserwebsocket.provider/.gitignore deleted file mode 100644 index 90dde36e4ac..00000000000 --- a/io.openems.backend.browserwebsocket.provider/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/bin/ -/bin_test/ -/generated/ diff --git a/io.openems.backend.browserwebsocket.provider/.project b/io.openems.backend.browserwebsocket.provider/.project deleted file mode 100644 index ee83ee8bf77..00000000000 --- a/io.openems.backend.browserwebsocket.provider/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - io.openems.backend.browserwebsocket.provider - - - - - - org.eclipse.jdt.core.javabuilder - - - - - bndtools.core.bndbuilder - - - - - - org.eclipse.jdt.core.javanature - bndtools.core.bndnature - - diff --git a/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index c00dc701bd2..00000000000 --- a/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java=UTF-8 -encoding//test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/debug.bndrun=UTF-8 -encoding/io.openems.backend.browserwebsocket.provider.bndrun=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537071b..00000000000 --- a/io.openems.backend.browserwebsocket.provider/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.browserwebsocket.provider/bnd.bnd b/io.openems.backend.browserwebsocket.provider/bnd.bnd deleted file mode 100644 index 0228b34f6c4..00000000000 --- a/io.openems.backend.browserwebsocket.provider/bnd.bnd +++ /dev/null @@ -1,29 +0,0 @@ -# -# io.openems.backend.browserwebsocket.provider PROVIDER BUNDLE -# - -Bundle-Version: 1.0.0.${tstamp} - - -Private-Package: \ - io.openems.backend.browserwebsocket.provider.internal,\ - io.openems.backend.browserwebsocket.provider - --includeresource: {readme.md} - --buildpath: \ - osgi.enroute.base.api;version=2.1,\ - io.openems.backend.browserwebsocket.api;version=latest,\ - com.google.guava,\ - com.google.gson,\ - io.openems.common;version=latest,\ - io.openems.backend.timedata.api;version=latest,\ - io.openems.backend.metadata.api;version=latest,\ - io.openems.backend.openemswebsocket.api;version=latest,\ - io.openems.wrapper.websocket;version=latest - --testpath: \ - osgi.enroute.junit.wrapper;version=4.12, \ - osgi.enroute.hamcrest.wrapper;version=1.3 - --runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' \ No newline at end of file diff --git a/io.openems.backend.browserwebsocket.provider/debug.bndrun b/io.openems.backend.browserwebsocket.provider/debug.bndrun deleted file mode 100644 index 969e63e6b05..00000000000 --- a/io.openems.backend.browserwebsocket.provider/debug.bndrun +++ /dev/null @@ -1,13 +0,0 @@ -# -# io.openems.backend.browserwebsocket.impl DEBUG LAUNCH SPECFICATION -# - --include: ~io.openems.backend.browserwebsocket.impl.bndrun - --runrequires.debug: \ - ${debug-bundles} - --runtrace: true - --runbundles: \ - ${error;Resolve first} diff --git a/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun b/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun deleted file mode 100644 index 33edb84cd55..00000000000 --- a/io.openems.backend.browserwebsocket.provider/io.openems.backend.browserwebsocket.provider.bndrun +++ /dev/null @@ -1,36 +0,0 @@ -# -# io.openems.backend.browserwebsocket.impl LAUNCH SPECIFICATION -# - - -Bundle-Version: 1.0.0.${tstamp} -Bundle-SymbolicName: io.openems.backend.browserwebsocket.provider.launch -JPM-Command: provider - - --runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.provider)' - --runbundles: \ - com.google.gson;version='[2.8.2,2.8.3)',\ - com.google.guava;version='[19.0.0,19.0.1)',\ - io.openems.backend.browserwebsocket.api;version=snapshot,\ - io.openems.backend.browserwebsocket.provider;version=snapshot,\ - io.openems.backend.metadata.api;version=snapshot,\ - io.openems.backend.metadata.odoo;version=snapshot,\ - io.openems.backend.openemswebsocket.api;version=snapshot,\ - io.openems.backend.openemswebsocket.provider;version=snapshot,\ - io.openems.backend.timedata.api;version=snapshot,\ - io.openems.backend.timedata.influx;version=snapshot,\ - io.openems.common;version=snapshot,\ - io.openems.wrapper.websocket;version=snapshot,\ - org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ - org.apache.felix.log;version='[1.0.1,1.0.2)',\ - org.apache.felix.scr;version='[2.0.2,2.0.3)',\ - org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ - org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ - org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ - org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ - org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - slf4j.api;version='[1.8.0,1.8.1)' diff --git a/io.openems.backend.browserwebsocket.provider/readme.md b/io.openems.backend.browserwebsocket.provider/readme.md deleted file mode 100644 index 8174d352e03..00000000000 --- a/io.openems.backend.browserwebsocket.provider/readme.md +++ /dev/null @@ -1,8 +0,0 @@ -# io.openems.backend.browserwebsocket.impl Provider - -${Bundle-Description} - -## Example - -## References - diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java deleted file mode 100644 index 088388fae59..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/BrowserWebsocketProvider.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.openems.backend.browserwebsocket.provider; - -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.log.LogService; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; -import org.slf4j.Logger; - -import io.openems.backend.browserwebsocket.provider.internal.BrowserWebsocket; -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; -import io.openems.backend.timedata.api.TimedataService; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.utils.Log; - -import org.osgi.service.metatype.annotations.Designate; - -@Designate(ocd = BrowserWebsocketProvider.Config.class, factory = false) -@Component(name = "BrowserWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class BrowserWebsocketProvider { - - @Reference - Logger log; - - @ObjectClassDefinition - @interface Config { - int port(); - } - - @Activate - void activate(Config config) { - System.out.println("Activate BrowserWebsocket"); - try { - this.browserWebsocket = new BrowserWebsocket(config.port()); - } catch (OpenemsException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Deactivate - void deactivate() { - System.out.println("Deactivate BrowserWebsocket"); - } - - private BrowserWebsocket browserWebsocket = null; - - @Reference - void setOpenemsWebsocketService(OpenemsWebsocketService openemsWebsocketService) { - this.browserWebsocket.setOpenemsWebsocketService(openemsWebsocketService); - } - - @Reference - void setMetadataService(MetadataService metadataService) { - this.browserWebsocket.setMetadataService(metadataService); - } - - @Reference - void setTimedataService(TimedataService timedataService) { - this.browserWebsocket.setTimedataService(timedataService); - } -} diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java deleted file mode 100644 index 7ef3d1f60ab..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BackendCurrentDataWorker.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.openems.backend.browserwebsocket.provider.internal; - - -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.osgi.service.component.annotations.Reference; - -import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; - -import io.openems.backend.timedata.api.TimedataService; -import io.openems.common.exceptions.NotImplementedException; -import io.openems.common.types.ChannelAddress; -import io.openems.common.utils.JsonUtils; -import io.openems.common.websocket.CurrentDataWorker; - -public class BackendCurrentDataWorker extends CurrentDataWorker { - - @Reference - TimedataService timedata; - - private final WebSocket websocket; - private final int deviceId; - - public BackendCurrentDataWorker(int deviceId, String deviceName, WebSocket websocket, JsonArray jId, - HashMultimap channels) { - super(jId, Optional.of(deviceName), channels); - this.deviceId = deviceId; - this.websocket = websocket; - } - - @Override - protected Optional getChannelValue(ChannelAddress channelAddress) { - Optional channelCacheOpt = timedata.getChannelValue(this.deviceId, channelAddress); - if (channelCacheOpt.isPresent()) { - try { - return Optional.ofNullable(JsonUtils.getAsJsonElement(channelCacheOpt.get())); - } catch (NotImplementedException e) { - return Optional.empty(); - } - } else { - return Optional.empty(); - } - } - - @Override - protected Optional getWebsocket() { - return Optional.of(this.websocket); - } - -} diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java deleted file mode 100644 index 6d7d5f0b58f..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSession.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.openems.backend.browserwebsocket.provider.internal; - -import io.openems.common.session.Session; - -public class BrowserSession extends Session { - - protected BrowserSession(String token, BrowserSessionData data) { - super(token, data); - } - - @Override - public String toString() { - return "User [" + getData().getUserName() + "] Session [" + getData().getOdooSessionId().orElse("") + "]"; - } -} diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java deleted file mode 100644 index eca97dec544..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionData.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.openems.backend.browserwebsocket.provider.internal; - -import java.util.Collection; -import java.util.Optional; -import java.util.Set; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.gson.JsonObject; - -import io.openems.common.session.SessionData; -import io.openems.common.types.DeviceImpl; - -public class BrowserSessionData extends SessionData { - private String userName = ""; - private Optional userId = Optional.empty(); - private Optional odooSessionIdOpt = Optional.empty(); - private LinkedHashMultimap devices = LinkedHashMultimap.create(); - private Optional currentDataWorkerOpt = Optional.empty(); - - public Optional getOdooSessionId() { - return odooSessionIdOpt; - } - - public void setOdooSessionId(Optional odooSessionIdOpt) { - this.odooSessionIdOpt = odooSessionIdOpt; - } - - public void setDevices(LinkedHashMultimap deviceMap) { - this.devices = deviceMap; - } - - public void setUserId(Integer userId) { - this.userId = Optional.of(userId); - } - - public void setUserName(String name) { - this.userName = name; - } - - public Optional getUserId() { - return userId; - } - - public String getUserName() { - return userName; - } - - public Set getDevices(String name) { - return this.devices.get(name); - } - - public Collection getDevices() { - return this.devices.values(); - } - - public void setCurrentDataWorkerOpt(BackendCurrentDataWorker currentDataWorker) { - this.currentDataWorkerOpt = Optional.ofNullable(currentDataWorker); - } - - public Optional getCurrentDataWorkerOpt() { - return currentDataWorkerOpt; - } - - @Override - public JsonObject toJsonObject() { - JsonObject j = new JsonObject(); - // TODO Auto-generated method stub - return j; - } -} diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java deleted file mode 100644 index e9f8f1d02e4..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserSessionManager.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.openems.backend.browserwebsocket.provider.internal; - -import io.openems.common.session.SessionManager; - -public class BrowserSessionManager extends SessionManager { - - @Override - public BrowserSession _createNewSession(String token, BrowserSessionData data) { - return new BrowserSession(token, data); - } -} diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java deleted file mode 100644 index 292cd8ee815..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/internal/BrowserWebsocket.java +++ /dev/null @@ -1,441 +0,0 @@ -package io.openems.backend.browserwebsocket.provider.internal; - -import java.util.Map.Entry; -import java.time.Period; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.Optional; -import java.util.Set; - -import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.handshake.ClientHandshake; - -import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; -import io.openems.backend.timedata.api.TimedataService; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.session.Role; -import io.openems.common.types.Device; -import io.openems.common.types.DeviceImpl; -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.Log; -import io.openems.common.utils.StringUtils; -import io.openems.common.websocket.AbstractWebsocketServer; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.LogBehaviour; -import io.openems.common.websocket.Notification; -import io.openems.common.websocket.WebSocketUtils; - -public class BrowserWebsocket - extends AbstractWebsocketServer { - - private MetadataService metadataService; - private OpenemsWebsocketService openemsWebsocketService; - private TimedataService timedataService; - - public BrowserWebsocket(int port) throws OpenemsException { - super(port, new BrowserSessionManager()); - } - - /** - * Open event of websocket. Parses the Odoo "session_id" and stores it in a new - * Session. - */ - @Override - protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - // Prepare session information - String error = ""; - BrowserSession session = null; - Optional sessionIdOpt = Optional.empty(); - - try { - // get cookie information - JsonObject jCookie = parseCookieFromHandshake(handshake); - sessionIdOpt = JsonUtils.getAsOptionalString(jCookie, "session_id"); - - // try to get token of an existing, valid session from cookie - if (jCookie.has("token")) { - String token = JsonUtils.getAsString(jCookie, "token"); - Optional existingSessionOpt = sessionManager.getSessionByToken(token); - if (existingSessionOpt.isPresent()) { - BrowserSession existingSession = existingSessionOpt.get(); - // test if it is the same Odoo session_id - if (sessionIdOpt.equals(existingSession.getData().getOdooSessionId())) { - session = existingSession; - } - } - } - } catch (OpenemsException e) { - error = e.getMessage(); - } - - if (session == null) { - // create new session if no existing one was found - BrowserSessionData sessionData = new BrowserSessionData(); - sessionData.setOdooSessionId(sessionIdOpt); - session = sessionManager.createNewSession(sessionData); - } - - // check if the session is now valid and send reply to browser - BrowserSessionData data = session.getData(); - - // check Odoo session and refresh info from Odoo - if (!data.getOdooSessionId().isPresent()) { - error = "Session-ID is missing."; - } else { - try { - this.metadataService.getInfoWithSession(session.getData().getOdooSessionId().get()); - } catch (OpenemsException e) { - error = e.getMessage(); - } - } - - if (error.isEmpty()) { - // add isOnline information - for (DeviceImpl device : data.getDevices()) { - device.setOnline(this.openemsWebsocketService.isWebsocketConnected(device.getName())); - } - - // send connection successful to browser - JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), Optional.empty(), - data.getDevices()); - // TODO write user name to log output - WebSocketUtils.send(websocket, jReply); - - // add websocket to local cache - this.addWebsocket(websocket, session); - - Log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") - + "]."); - - } else { - // send connection failed to browser - JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); - WebSocketUtils.send(websocket, jReply); - Log.warn("User [" + data.getUserName() + "] connection failed. Session [" - + data.getOdooSessionId().orElse("") + "] Error [" + error + "]."); - - websocket.closeConnection(CloseFrame.REFUSE, error); - } - } - - /** - * Message event of websocket. Handles a new message. - */ - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - /* - * With existing device name - */ - if (deviceNameOpt.isPresent()) { - String deviceName = deviceNameOpt.get(); - Optional deviceIdOpt = Device.parseNumberFromName(deviceName); - /* - * Query historic data - */ - if (jMessage.has("historicData")) { - // parse deviceId - JsonArray jMessageId = jMessageIdOpt.get(); - try { - JsonObject jHistoricData = JsonUtils.getAsJsonObject(jMessage, "historicData"); - JsonObject jReply = historicData(jMessageId, jHistoricData, deviceIdOpt, - this.timedataService, Role.ADMIN); - // TODO read role from device - WebSocketUtils.send(websocket, jReply); - } catch (OpenemsException e) { - Log.error(e.getMessage()); - } - } - - // get session - Optional sessionOpt = this.getSessionFromWebsocket(websocket); - if (!sessionOpt.isPresent()) { - Log.warn("No BrowserSession available."); - // throw new OpenemsException("No BrowserSession available."); - } - BrowserSession session = sessionOpt.get(); - - /* - * Subscribe to currentData - */ - if (jMessage.has("currentData")) { - JsonObject jCurrentData; - try { - jCurrentData = JsonUtils.getAsJsonObject(jMessage, "currentData"); - Log.info("User [" + session.getData().getUserName() + "] subscribed to current data for device [" - + deviceName + "]: " + StringUtils.toShortString(jCurrentData, 50)); - JsonArray jMessageId = jMessageIdOpt.get(); - int deviceId = deviceIdOpt.get(); - this.currentData(session, websocket, jCurrentData, jMessageId, deviceName, deviceId); - } catch (OpenemsException e) { - Log.error(e.getMessage()); - } - } - - /* - * Serve "Config -> Query" from cache - */ - Optional configModeOpt = Optional.empty(); - if (jMessage.has("config")) { - Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); - if (jConfigOpt.isPresent()) { - configModeOpt = JsonUtils.getAsOptionalString(jConfigOpt.get(), "mode"); - if (configModeOpt.isPresent() && configModeOpt.get().equals("query")) { - /* - * Query current config - */ - Optional openemsConfig = this.openemsWebsocketService.getConfig(deviceName); - if (!openemsConfig.isPresent()) { - // set configMode to empty, so that the request is forwarded to Edge - configModeOpt = Optional.empty(); - } else { - Log.info("User [" + session.getData().getUserName() - + "]: Sent OpenEMS-Config from cache for device [" + deviceName + "]."); - JsonObject jReply = DefaultMessages.configQueryReply(openemsConfig.get()); - if (deviceNameOpt.isPresent()) { - jReply.addProperty("device", deviceNameOpt.get()); - } - if (jMessageIdOpt.isPresent()) { - jReply.add("id", jMessageIdOpt.get()); - } - WebSocketUtils.send(websocket, jReply); - } - } - } - } - - /* - * Forward to OpenEMS Edge - */ - if ((jMessage.has("config") && !configModeOpt.orElse("").equals("query")) || jMessage.has("log") - || jMessage.has("system")) { - try { - forwardMessageToOpenems(session, websocket, jMessage, deviceName); - } catch (OpenemsException e) { - WebSocketUtils.sendNotification(websocket, new JsonArray(), LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_UNABLE_TO_FORWARD, deviceName, e.getMessage()); - } - } - } - } - - @Override - protected void _onClose(WebSocket websocket, Optional sessionOpt) { - // nothing to do. Session is kept open. - } - - /** - * Forward message to OpenEMS websocket. - * - * @throws OpenemsException - */ - private void forwardMessageToOpenems(BrowserSession session, WebSocket websocket, JsonObject jMessage, - String deviceName) throws OpenemsException { - // remove device from message - if (jMessage.has("device")) { - jMessage.remove("device"); - } - - // add session token to message id for identification - JsonArray jId; - if (jMessage.has("id")) { - jId = JsonUtils.getAsJsonArray(jMessage, "id"); - } else { - jId = new JsonArray(); - } - jId.add(session.getToken()); - jMessage.add("id", jId); - - // add authentication role - Role role = Role.GUEST; - for (DeviceImpl device : session.getData().getDevices(deviceName)) { - role = device.getRole(); - } - jMessage.addProperty("role", role.name().toLowerCase()); - - // get OpenEMS websocket and forward message - this.openemsWebsocketService.send(deviceName, jMessage); - } - - /** - * Handle current data subscriptions (copied from EdgeWebsocketHandler. Try to - * keep synced...) - * - * @param j - */ - private synchronized void currentData(BrowserSession session, WebSocket websocket, JsonObject jCurrentData, - JsonArray jId, String deviceName, int deviceId) { - try { - String mode = JsonUtils.getAsString(jCurrentData, "mode"); - - if (mode.equals("subscribe")) { - /* - * Subscribe to channels - */ - - // remove old worker if existed - Optional workerOpt = session.getData().getCurrentDataWorkerOpt(); - if (workerOpt.isPresent()) { - session.getData().setCurrentDataWorkerOpt(null); - workerOpt.get().dispose(); - } - - // parse subscribed channels - HashMultimap channels = HashMultimap.create(); - JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); - for (Entry entry : jSubscribeChannels.entrySet()) { - String thing = entry.getKey(); - JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement jChannel : jChannels) { - String channel = JsonUtils.getAsString(jChannel); - channels.put(thing, channel); - } - } - if (!channels.isEmpty()) { - // create new worker - BackendCurrentDataWorker worker = new BackendCurrentDataWorker(deviceId, deviceName, websocket, jId, - channels); - session.getData().setCurrentDataWorkerOpt(worker); - } - } - } catch (OpenemsException e) { - Log.warn(e.getMessage()); - } - } - - // TODO notification handling - // /** - // * Generates a generic notification message - // * - // * @param message - // * @return - // */ - // private JsonObject generateNotification(String message) { - // JsonObject j = new JsonObject(); - // JsonObject jNotification = new JsonObject(); - // jNotification.addProperty("message", message); - // j.add("notification", jNotification); - // return j; - // } - - // TODO system command - // /** - // * System command - // * - // * @param j - // */ - // private synchronized void system(String deviceName, JsonElement - // jSubscribeElement) { - // JsonObject j = new JsonObject(); - // j.add("system", jSubscribeElement); - // Optional openemsWebsocketOpt = ConnectionManager.instance() - // .getOpenemsWebsocketFromDeviceName(deviceName); - // if (!openemsWebsocketOpt.isPresent()) { - // log.warn("Trying to forward system call to [" + deviceName + "], but it is - // not online"); - // } - // WebSocket openemsWebsocket = openemsWebsocketOpt.get(); - // log.info(deviceName + ": forward system call to OpenEMS " + - // StringUtils.toShortString(j, 100)); - // WebSocketUtils.send(openemsWebsocket, j); - // } - - /** - * OpenEMS Websocket tells us, when the connection to an OpenEMS Edge is closed - * - * @param name - */ - public void openemsConnectionClosed(String name) { - for (BrowserSession session : this.sessionManager.getSessions()) { - for (DeviceImpl device : session.getData().getDevices()) { - if (name.equals(device.getName())) { - Optional websocketOpt = this.getWebsocketFromSession(session); - WebSocketUtils.sendNotification(websocketOpt, new JsonArray(), LogBehaviour.DO_NOT_WRITE_TO_LOG, - Notification.EDGE_CONNECTION_ClOSED, name); - } - } - } - } - - /** - * OpenEMS Websocket tells us, when the connection to an OpenEMS Edge is openend - * - * @param name - */ - public void openemsConnectionOpened(Set names) { - for (BrowserSession session : this.sessionManager.getSessions()) { - for (DeviceImpl device : session.getData().getDevices()) { - for (String name : names) { - if (name.equals(device.getName())) { - // Optional websocketOpt = this.getWebsocketFromSession(session); - // TODO re-enable this once it is stable - // WebSocketUtils.sendNotification(websocketOpt, new JsonArray(), - // LogBehaviour.DO_NOT_WRITE_TO_LOG, - // Notification.EDGE_CONNECTION_OPENED, name); - } - } - } - } - } - - /** - * Query history command - * - * @param j - */ - private JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, - TimedataService timedataService, Role role) { - try { - String mode = JsonUtils.getAsString(jHistoricData, "mode"); - if (mode.equals("query")) { - /* - * Query historic data - */ - int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); - ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); - ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); - ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); - JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); - // TODO check if role is allowed to read these channels - // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); - int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); - // TODO: better calculation of sensible resolution - int resolution = 10 * 60; // 10 Minutes - if (days > 25) { - resolution = 24 * 60 * 60; // 1 Day - } else if (days > 6) { - resolution = 3 * 60 * 60; // 3 Hours - } else if (days > 2) { - resolution = 60 * 60; // 60 Minutes - } - JsonArray jData = timedataService.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); - // send reply - return DefaultMessages.historicDataQueryReply(jMessageId, jData); - } - } catch (Exception e) { - Log.error("HistoricData Error: ", e); - e.printStackTrace(); - } - return new JsonObject(); - } - - public void setMetadataService(MetadataService metadataService) { - this.metadataService = metadataService; - } - - public void setOpenemsWebsocketService(OpenemsWebsocketService openemsWebsocketService) { - this.openemsWebsocketService = openemsWebsocketService; - } - - public void setTimedataService(TimedataService timedataService) { - this.timedataService = timedataService; - } -} diff --git a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java b/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java deleted file mode 100644 index 5a448855f85..00000000000 --- a/io.openems.backend.browserwebsocket.provider/src/io/openems/backend/browserwebsocket/provider/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.browserwebsocket.provider; diff --git a/io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java b/io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java deleted file mode 100644 index 51216763abb..00000000000 --- a/io.openems.backend.browserwebsocket.provider/test/io/openems/backend/browserwebsocket/impl/ProviderImplTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.openems.backend.browserwebsocket.impl; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; - -import io.openems.backend.browserwebsocket.provider.BrowserWebsocketProvider; - -/* - * Example JUNit test case - * - */ - -public class ProviderImplTest { - - /* - * Example test method - */ - - @Test - public void simple() { - BrowserWebsocketProvider impl = new BrowserWebsocketProvider(); - assertNotNull(impl); - } - -} diff --git a/io.openems.backend.metadata.odoo.provider/bnd.bnd b/io.openems.backend.metadata.odoo.provider/bnd.bnd index 11d07d8ae2d..5d4534e14b3 100644 --- a/io.openems.backend.metadata.odoo.provider/bnd.bnd +++ b/io.openems.backend.metadata.odoo.provider/bnd.bnd @@ -23,5 +23,5 @@ Private-Package: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 -Export-Package: io.openems.backend.metadata.api +Export-Package: io.openems.backend.metadata.api;-provide=true -runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.api/bnd.bnd b/io.openems.backend.openemswebsocket.api/bnd.bnd index 90e51ddd616..5a0fc623572 100644 --- a/io.openems.backend.openemswebsocket.api/bnd.bnd +++ b/io.openems.backend.openemswebsocket.api/bnd.bnd @@ -3,16 +3,21 @@ # Bundle-Version: 1.0.0.${tstamp} +Bundle-Description: \ + This project contains a complete example with API, provider, and JUnit test code. \ + \ + ${warning;Please update this Bundle-Description in bnd.bnd} Export-Package: \ io.openems.backend.openemswebsocket.api +Require-Capability: \ + compile-only + -includeresource: {readme.md} --buildpath: \ - osgi.enroute.base.api;version=2.1,\ - com.google.gson,\ - io.openems.common;version=latest +-buildpath: \ + osgi.enroute.base.api;version=2.1 -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java index 2852c875d4c..5665ded938e 100644 --- a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java +++ b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java @@ -1,19 +1,7 @@ package io.openems.backend.openemswebsocket.api; -import java.util.Optional; - import org.osgi.annotation.versioning.ProviderType; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; - @ProviderType public interface OpenemsWebsocketService { - - boolean isWebsocketConnected(String name); - - Optional getConfig(String deviceName); - - void send(String deviceName, JsonObject j) throws OpenemsException; } diff --git a/io.openems.backend.browserwebsocket.api/.classpath b/io.openems.backend.openemswebsocket.impl.provider/.classpath similarity index 100% rename from io.openems.backend.browserwebsocket.api/.classpath rename to io.openems.backend.openemswebsocket.impl.provider/.classpath diff --git a/io.openems.backend.browserwebsocket.api/.gitignore b/io.openems.backend.openemswebsocket.impl.provider/.gitignore similarity index 100% rename from io.openems.backend.browserwebsocket.api/.gitignore rename to io.openems.backend.openemswebsocket.impl.provider/.gitignore diff --git a/io.openems.backend.browserwebsocket.api/.project b/io.openems.backend.openemswebsocket.impl.provider/.project similarity index 88% rename from io.openems.backend.browserwebsocket.api/.project rename to io.openems.backend.openemswebsocket.impl.provider/.project index 6a7fcd32642..d77c8f344fc 100644 --- a/io.openems.backend.browserwebsocket.api/.project +++ b/io.openems.backend.openemswebsocket.impl.provider/.project @@ -1,6 +1,6 @@ - io.openems.backend.browserwebsocket.api + io.openems.backend.openemswebsocket.impl.provider diff --git a/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..e0ad58c308d --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java=UTF-8 +encoding//src/io/openems/backend/openemswebsocket/impl/provider/package-info.java=UTF-8 +encoding//test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.openemswebsocket.provider.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.openemswebsocket.impl.provider/bnd.bnd b/io.openems.backend.openemswebsocket.impl.provider/bnd.bnd new file mode 100644 index 00000000000..7945141e0e6 --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/bnd.bnd @@ -0,0 +1,21 @@ +# +# io.openems.backend.openemswebsocket.provider PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: io.openems.backend.openemswebsocket.api + + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.openemswebsocket.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' +Private-Package: io.openems.backend.openemswebsocket.impl.provider \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.impl.provider/debug.bndrun b/io.openems.backend.openemswebsocket.impl.provider/debug.bndrun new file mode 100644 index 00000000000..fc23c8daa3e --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.openemswebsocket.provider DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.openemswebsocket.provider.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun b/io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun new file mode 100644 index 00000000000..f5ae5e60843 --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun @@ -0,0 +1,20 @@ +# +# io.openems.backend.openemswebsocket.provider LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.openemswebsocket.provider.launch +JPM-Command: provider + + + +-runbundles: \ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + io.openems.backend.openemswebsocket.impl.provider;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + slf4j.api;version='[1.8.0,1.8.1)' +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.impl.provider)' \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.impl.provider/readme.md b/io.openems.backend.openemswebsocket.impl.provider/readme.md new file mode 100644 index 00000000000..80cc3618949 --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.openemswebsocket.provider Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java new file mode 100644 index 00000000000..5976eecb3dc --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java @@ -0,0 +1,35 @@ +package io.openems.backend.openemswebsocket.impl.provider; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; + +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@Designate(ocd = OpenemsWebsocket.Config.class, factory = false) +@Component(name = "OpenemsWebsocket") +public class OpenemsWebsocket implements OpenemsWebsocketService { + + private final Logger log = LoggerFactory.getLogger(OpenemsWebsocket.class); + + @ObjectClassDefinition + @interface Config { + int port(); + } + + @Activate + void activate(Config config) { + log.debug("Activate OpenemsWebsocket"); + } + + @Deactivate + void deactivate() { + log.debug("Deactivate OpenemsWebsocket"); + } + +} diff --git a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java new file mode 100644 index 00000000000..56ed60ff9d0 --- /dev/null +++ b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.openemswebsocket.impl.provider; diff --git a/io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java b/io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java similarity index 55% rename from io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java rename to io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java index b336a053e9a..3b0909af43b 100644 --- a/io.openems.backend.openemswebsocket.provider/test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java +++ b/io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java @@ -1,10 +1,10 @@ -package io.openems.backend.openemswebsocket.impl; +package io.openems.backend.openemswebsocket.provider; import static org.junit.Assert.assertNotNull; import org.junit.Test; -import io.openems.backend.openemswebsocket.provider.OpenemsWebsocketProvider; +import io.openems.backend.openemswebsocket.impl.provider.OpenemsWebsocket; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - OpenemsWebsocketProvider impl = new OpenemsWebsocketProvider(); + OpenemsWebsocket impl = new OpenemsWebsocket(); assertNotNull(impl); } diff --git a/io.openems.backend.openemswebsocket.provider/.classpath b/io.openems.backend.openemswebsocket.provider/.classpath deleted file mode 100644 index 26009f42341..00000000000 --- a/io.openems.backend.openemswebsocket.provider/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/io.openems.backend.openemswebsocket.provider/.gitignore b/io.openems.backend.openemswebsocket.provider/.gitignore deleted file mode 100644 index 90dde36e4ac..00000000000 --- a/io.openems.backend.openemswebsocket.provider/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/bin/ -/bin_test/ -/generated/ diff --git a/io.openems.backend.openemswebsocket.provider/.project b/io.openems.backend.openemswebsocket.provider/.project deleted file mode 100644 index aade4e07a07..00000000000 --- a/io.openems.backend.openemswebsocket.provider/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - io.openems.backend.openemswebsocket.provider - - - - - - org.eclipse.jdt.core.javabuilder - - - - - bndtools.core.bndbuilder - - - - - - org.eclipse.jdt.core.javanature - bndtools.core.bndnature - - diff --git a/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index c1550120c06..00000000000 --- a/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java=UTF-8 -encoding//test/io/openems/backend/openemswebsocket/impl/ProviderImplTest.java=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/debug.bndrun=UTF-8 -encoding/io.openems.backend.openemswebsocket.impl.bndrun=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537071b..00000000000 --- a/io.openems.backend.openemswebsocket.provider/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.openemswebsocket.provider/bnd.bnd b/io.openems.backend.openemswebsocket.provider/bnd.bnd deleted file mode 100644 index 5890b9d429b..00000000000 --- a/io.openems.backend.openemswebsocket.provider/bnd.bnd +++ /dev/null @@ -1,28 +0,0 @@ -# -# io.openems.backend.openemswebsocket.impl PROVIDER BUNDLE -# - -Bundle-Version: 1.0.0.${tstamp} - - -Private-Package: io.openems.backend.openemswebsocket.provider.internal - --includeresource: {readme.md} - --buildpath: \ - osgi.enroute.base.api;version=2.1,\ - io.openems.backend.openemswebsocket.api;version=latest,\ - io.openems.backend.common;version=latest,\ - io.openems.common;version=latest,\ - io.openems.backend.metadata.api;version=latest,\ - com.google.gson,\ - com.google.guava,\ - io.openems.backend.browserwebsocket.api;version=latest,\ - io.openems.backend.timedata.api;version=latest,\ - io.openems.wrapper.websocket;version=latest - --testpath: \ - osgi.enroute.junit.wrapper;version=4.12, \ - osgi.enroute.hamcrest.wrapper;version=1.3 - -Export-Package: io.openems.backend.openemswebsocket.provider \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.provider/debug.bndrun b/io.openems.backend.openemswebsocket.provider/debug.bndrun deleted file mode 100644 index d9b363fb482..00000000000 --- a/io.openems.backend.openemswebsocket.provider/debug.bndrun +++ /dev/null @@ -1,13 +0,0 @@ -# -# io.openems.backend.openemswebsocket.impl DEBUG LAUNCH SPECFICATION -# - --include: ~io.openems.backend.openemswebsocket.impl.bndrun - --runrequires.debug: \ - ${debug-bundles} - --runtrace: true - --runbundles: \ - ${error;Resolve first} diff --git a/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved b/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved new file mode 100644 index 00000000000..d214db2741a --- /dev/null +++ b/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved @@ -0,0 +1,7 @@ +io.openems.backend.openemswebsocket.impl.provider;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.backend.openemswebsocket.impl.provider/generated/io.openems.backend.openemswebsocket.impl.provider.jar +org.osgi.service.metatype;version='[1.3.0,1.3.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/osgi/org.osgi.service.metatype/1.3.0/org.osgi.service.metatype-1.3.0.jar +slf4j.api;version='[1.8.0,1.8.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/slf4j/slf4j-api/1.8.0-beta1/slf4j-api-1.8.0-beta1.jar +org.apache.felix.log;version='[1.0.1,1.0.2)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.log/1.0.1/org.apache.felix.log-1.0.1.jar +org.apache.felix.scr;version='[2.0.2,2.0.3)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.scr/2.0.2/org.apache.felix.scr-2.0.2.jar +org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/osgi/org.eclipse.equinox.metatype/1.4.100.v20150408-1437/org.eclipse.equinox.metatype-1.4.100.v20150408-1437.jar +org.apache.felix.configadmin;version='[1.8.8,1.8.9)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.configadmin/1.8.8/org.apache.felix.configadmin-1.8.8.jar diff --git a/io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun b/io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun deleted file mode 100644 index 2a8c294c657..00000000000 --- a/io.openems.backend.openemswebsocket.provider/io.openems.backend.openemswebsocket.impl.bndrun +++ /dev/null @@ -1,14 +0,0 @@ -# -# io.openems.backend.openemswebsocket.impl LAUNCH SPECIFICATION -# - - -Bundle-Version: 1.0.0.${tstamp} -Bundle-SymbolicName: io.openems.backend.openemswebsocket.impl.launch -JPM-Command: provider - - --runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.impl.provider)' - --runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.openemswebsocket.provider/readme.md b/io.openems.backend.openemswebsocket.provider/readme.md deleted file mode 100644 index 211a2be5b69..00000000000 --- a/io.openems.backend.openemswebsocket.provider/readme.md +++ /dev/null @@ -1,8 +0,0 @@ -# io.openems.backend.openemswebsocket.impl Provider - -${Bundle-Description} - -## Example - -## References - diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java deleted file mode 100644 index c484180272a..00000000000 --- a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/OpenemsWebsocketProvider.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.openems.backend.openemswebsocket.provider; - -import java.util.Optional; - -import org.java_websocket.WebSocket; -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.metatype.annotations.ObjectClassDefinition; - -import com.google.gson.JsonObject; - -import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; -import io.openems.backend.openemswebsocket.provider.internal.OpenemsSession; -import io.openems.backend.openemswebsocket.provider.internal.OpenemsWebsocket; -import io.openems.backend.timedata.api.TimedataService; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.websocket.WebSocketUtils; - -import org.osgi.service.metatype.annotations.Designate; - -@Designate(ocd = OpenemsWebsocketProvider.Config.class, factory = false) -@Component(name = "OpenemsWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class OpenemsWebsocketProvider implements OpenemsWebsocketService { - - @ObjectClassDefinition - @interface Config { - int port(); - } - - @Activate - void activate(Config config) { - System.out.println("Activate OpenemsWebsocket"); - this.openemsWebsocket = new OpenemsWebsocket(config.port()); - } - - @Deactivate - void deactivate() { - System.out.println("Deactivate OpenemsWebsocket"); - } - - private OpenemsWebsocket openemsWebsocket = null; - - @Reference - void setMetadataService(MetadataService metadataService) { - this.openemsWebsocket.setMetadataService(metadataService); - } - - @Reference(cardinality = ReferenceCardinality.OPTIONAL) // avoids recursive dependency - void setBrowserWebsocketService(BrowserWebsocketService browserWebsocketService) { - this.openemsWebsocket.setBrowserWebsocketService(browserWebsocketService); - } - - @Reference - void setTimedataService(TimedataService timedataService) { - this.openemsWebsocket.setTimedataService(timedataService); - } - - @Override - public boolean isWebsocketConnected(String name) { - // TODO Auto-generated method stub - return false; - } - - @Override - public Optional getConfig(String deviceName) { - Optional openemsSessionOpt = this.openemsWebsocket.getOpenemsSession(deviceName); - if (openemsSessionOpt.isPresent()) { - return openemsSessionOpt.get().getData().getOpenemsConfigOpt(); - } - return Optional.empty(); - } - - @Override - public void send(String deviceName, JsonObject j) throws OpenemsException { - Optional openemsWebsocketOpt = this.openemsWebsocket.getOpenemsWebsocket(deviceName); - if (openemsWebsocketOpt.isPresent()) { - WebSocket openemsWebsocket = openemsWebsocketOpt.get(); - if (WebSocketUtils.send(openemsWebsocket, j)) { - return; - } else { - throw new OpenemsException("Sending failed"); - } - } else { - throw new OpenemsException("Device is not connected."); - } - } -} diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java deleted file mode 100644 index aa36927f646..00000000000 --- a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSession.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.openems.backend.openemswebsocket.provider.internal; - -import io.openems.common.session.Session; - -public class OpenemsSession extends Session { - - protected OpenemsSession(String token, OpenemsSessionData data) { - super(token, data); - } - - @Override - public String toString() { - return "Device [" + getData().getDevices().getNamesString() + "]"; - } -} diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java deleted file mode 100644 index 3ad5a85dc18..00000000000 --- a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionData.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.openems.backend.openemswebsocket.provider.internal; - -import java.util.Optional; - -import org.java_websocket.WebSocket; - -import com.google.gson.JsonObject; - -import io.openems.backend.metadata.api.MetadataDevices; -import io.openems.common.session.SessionData; - -public class OpenemsSessionData extends SessionData { - private final MetadataDevices devices; - private final WebSocket websocket; - private Optional openemsConfigOpt = Optional.empty(); - - public OpenemsSessionData(WebSocket websocket, MetadataDevices devices) { - this.devices = devices; - this.websocket = websocket; - } - - public WebSocket getWebsocket() { - return websocket; - } - - public MetadataDevices getDevices() { - return devices; - } - - @Override - public JsonObject toJsonObject() { - JsonObject j = new JsonObject(); - j.add("devices", this.devices.toJson()); - return j; - } - - public void setOpenemsConfig(JsonObject openemsConfig) { - this.openemsConfigOpt = Optional.ofNullable(openemsConfig); - } - - public Optional getOpenemsConfigOpt() { - return openemsConfigOpt; - } -} diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java deleted file mode 100644 index 58127d36782..00000000000 --- a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsSessionManager.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.openems.backend.openemswebsocket.provider.internal; - -import java.util.Map.Entry; -import java.util.Optional; - -import com.google.common.collect.HashMultimap; - -import io.openems.common.session.SessionManager; - -public class OpenemsSessionManager extends SessionManager { - - // mapping between Token (apikey) and device name - private final HashMultimap token2names = HashMultimap.create(); - - @Override - public OpenemsSession _createNewSession(String token, OpenemsSessionData data) { - return new OpenemsSession(token, data); - } - - @Override - protected void _putSession(String token, OpenemsSession session) { - super._putSession(token, session); - synchronized (this.token2names) { - for (String deviceName : session.getData().getDevices().getNames()) { - this.token2names.put(token, deviceName); - } - } - } - - @Override - protected void _removeSession(String token) { - super._removeSession(token); - synchronized (this.token2names) { - this.token2names.removeAll(token); - } - } - - public Optional getSessionByDeviceName(String name) { - String token = null; - synchronized (this.token2names) { - if (this.token2names.containsValue(name)) { - for (Entry entry : this.token2names.entries()) { - if (entry.getValue().equals(name)) { - token = entry.getKey(); - } - } - } - } - if (token == null) { - return Optional.empty(); - } - return super.getSessionByToken(token); - } -} diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java deleted file mode 100644 index d0fe02880c6..00000000000 --- a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/internal/OpenemsWebsocket.java +++ /dev/null @@ -1,347 +0,0 @@ -package io.openems.backend.openemswebsocket.provider.internal; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.handshake.ClientHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; -import io.openems.backend.metadata.api.MetadataDevice; -import io.openems.backend.metadata.api.MetadataDevices; -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.timedata.api.TimedataService; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.StringUtils; -import io.openems.common.websocket.AbstractWebsocketServer; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.WebSocketUtils; - -/** - * Handles connections to OpenEMS-Devices. - * - * @author stefan.feilmeier - * - */ -public class OpenemsWebsocket - extends AbstractWebsocketServer { - private final Logger log = LoggerFactory.getLogger(OpenemsWebsocket.class); - private MetadataService metadataService; - private BrowserWebsocketService browserWebsocketService; - private TimedataService timedataService; - - public OpenemsWebsocket(int port) { - super(port, new OpenemsSessionManager()); - } - - /** - * Open event of websocket. Parses the "apikey" and stores it in a new Session. - */ - @Override - protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - String apikey = ""; - Set deviceNames = new HashSet<>(); - try { - - // get apikey from handshake - Optional apikeyOpt = parseApikeyFromHandshake(handshake); - if (!apikeyOpt.isPresent()) { - throw new OpenemsException("Apikey is missing in handshake"); - } - apikey = apikeyOpt.get(); - - // if existing: close existing websocket for this apikey - Optional oldSessionOpt = this.sessionManager.getSessionByToken(apikey); - if (oldSessionOpt.isPresent()) { - OpenemsSession oldSession = oldSessionOpt.get(); - WebSocket oldWebsocket = oldSession.getData().getWebsocket(); - oldWebsocket.closeConnection(CloseFrame.REFUSE, - "Another device with this apikey [" + apikey + "] connected."); - this.sessionManager.removeSession(oldSession); - } - - // get device for apikey - MetadataDevices devices = this.metadataService.getDeviceModel().getDevicesForApikey(apikey); - if (devices.isEmpty()) { - throw new OpenemsException("Unable to find device for apikey [" + apikey + "]"); - } - deviceNames = devices.getNames(); - - // create new session - OpenemsSessionData sessionData = new OpenemsSessionData(websocket, devices); - OpenemsSession session = sessionManager.createNewSession(apikey, sessionData); - - // send successful reply to openems - JsonObject jReply = DefaultMessages.openemsConnectionSuccessfulReply(); - WebSocketUtils.send(websocket, jReply); - // add websocket to local cache - this.addWebsocket(websocket, session); - - log.info("Device [" + String.join(",", deviceNames) + "] connected."); - - try { - // set device active (in Odoo) - for (MetadataDevice device : devices) { - if (device.getState().equals("inactive")) { - device.setState("active"); - } - device.setLastMessage(); - device.writeObject(); - } - } catch (OpenemsException e) { - // this error does not stop the connection - log.error("Device [" + String.join(",", deviceNames) + "] error: " + e.getMessage()); - } - - // announce browserWebsocket that this OpenEMS Edge was connected - this.browserWebsocketService.openemsConnectionOpened(deviceNames); - - } catch (OpenemsException e) { - // send connection failed to OpenEMS - JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); - WebSocketUtils.send(websocket, jReply); - // close websocket - websocket.closeConnection(CloseFrame.REFUSE, "OpenEMS connection failed. Device [" - + String.join(",", deviceNames) + "] Apikey [" + apikey + "]"); - } - } - - /** - * Close event of websocket. Removes the session. - */ - @Override - public void _onClose(WebSocket websocket, Optional sessionOpt) { - if (sessionOpt.isPresent()) { - // log.info("Would remove the session... " + sessionOpt.get()); - sessionManager.removeSession(sessionOpt.get()); - } - } - - /** - * Message event of websocket. Handles a new message. At this point the device - * is already authenticated. - */ - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - OpenemsSessionData sessionData = this.getSessionFromWebsocket(websocket).get().getData(); - MetadataDevices devices = sessionData.getDevices(); - - // if (!jMessage.has("timedata") && !jMessage.has("currentData") && - // !jMessage.has("log") - // && !jMessage.has("config")) { - // log.info("Received from " + device.getName() + ": " + jMessage.toString()); - // } - - /* - * Config? -> store in Metadata - */ - if (jMessage.has("config")) { - try { - JsonObject jConfig = JsonUtils.getAsJsonObject(jMessage, "config"); - for (MetadataDevice device : devices) { - device.setOpenemsConfig(jConfig); - } - sessionData.setOpenemsConfig(jConfig); - log.info("Device [" + devices.getNamesString() + "] sent config."); - } catch (OpenemsException e) { - log.error(e.getMessage()); - } - } - - /* - * Is this a reply? -> forward to Browser - */ - if (jMessage.has("id")) { - for (String deviceName : devices.getNames()) { - forwardReplyToBrowser(websocket, deviceName, jMessage); - } - } - - /* - * New timestamped data - */ - if (jMessage.has("timedata")) { - timedata(devices, jMessage.get("timedata")); - } - - // Save data to Odoo - try { - for (MetadataDevice device : devices) { - device.writeObject(); - } - } catch (OpenemsException e) { - log.error("Device [" + devices.getNamesString() + "] error: " + e.getMessage()); - } - } - - private void forwardReplyToBrowser(WebSocket openemsWebsocket, String deviceName, JsonObject jMessage) { - try { - // get browser websocket - JsonArray jId = JsonUtils.getAsJsonArray(jMessage, "id"); - String token = JsonUtils.getAsString(jId.get(jId.size() - 1)); - Optional browserWebsocketOpt = this.browserWebsocketService.getWebsocketByToken(token); - if (!browserWebsocketOpt.isPresent()) { - log.warn("Device [" + deviceName + "] Browser websocket is not connected. Message [" - + StringUtils.toShortString(jMessage, 100) + "]"); - if (jMessage.has("currentData")) { - // unsubscribe obsolete browser websocket - WebSocketUtils.send(openemsWebsocket, DefaultMessages.currentDataSubscribe(jId, new JsonObject())); - } - if (jMessage.has("log")) { - // unsubscribe obsolete browser websocket - WebSocketUtils.send(openemsWebsocket, DefaultMessages.logUnsubscribe(jId)); - } - return; - } - WebSocket browserWebsocket = browserWebsocketOpt.get(); - - // remove token from message id - jId.remove(jId.size() - 1); - jMessage.add("id", jId); - // always add device name - jMessage.addProperty("device", deviceName); - - // send - WebSocketUtils.send(browserWebsocket, jMessage); - } catch (OpenemsException e) { - log.error("Device [" + deviceName + "] error: " + e.getMessage()); - } - } - - private void timedata(MetadataDevices devices, JsonElement jTimedataElement) { - try { - JsonObject jTimedata = JsonUtils.getAsJsonObject(jTimedataElement); - // Write to InfluxDB - try { - this.timedataService.write(devices, jTimedata); - log.debug(devices.getNamesString() + ": wrote " + jTimedata.entrySet().size() + " timestamps " - + StringUtils.toShortString(jTimedata, 120)); - } catch (Exception e) { - log.error("Unable to write Timedata: ", e); - } - // Set Odoo last message timestamp - for (MetadataDevice device : devices) { - device.setLastMessage(); - } - - for (Entry jTimedataEntry : jTimedata.entrySet()) { - try { - JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue()); - - // set Odoo last update timestamp only for those channels - for (String channel : jChannels.keySet()) { - if (channel.endsWith("ActivePower") - || channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2") - | channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) { - for (MetadataDevice device : devices) { - device.setLastUpdate(); - } - } - } - - // set specific Odoo values - if (jChannels.has("ess0/Soc")) { - int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt(); - for (MetadataDevice device : devices) { - device.setSoc(soc); - } - } - if (jChannels.has("system0/PrimaryIpAddress")) { - String ipv4 = JsonUtils.getAsPrimitive(jChannels, "system0/PrimaryIpAddress").getAsString(); - for (MetadataDevice device : devices) { - device.setIpV4(ipv4); - } - } - } catch (OpenemsException e) { - log.error("Device [" + String.join(",", devices.getNames()) + "] error: " + e.getMessage()); - } - } - - } catch (OpenemsException e) { - log.error("Device [" + devices.getNamesString() + "] error: " + e.getMessage()); - } - } - - /** - * Parses the apikey from websocket onOpen handshake - * - * @param handshake - * @return - */ - private Optional parseApikeyFromHandshake(ClientHandshake handshake) { - if (handshake.hasFieldValue("apikey")) { - String apikey = handshake.getFieldValue("apikey"); - return Optional.ofNullable(apikey); - } - return Optional.empty(); - } - - /** - * Returns true if this device is currently connected - * - * @param name - * @return - */ - public Optional getOpenemsSession(String deviceName) { - return this.sessionManager.getSessionByDeviceName(deviceName); - } - - /** - * Returns true if this device is currently connected - * - * @param name - * @return - */ - public boolean isOpenemsWebsocketConnected(String deviceName) { - Optional sessionOpt = getOpenemsSession(deviceName); - if (sessionOpt.isPresent()) { - return true; - } else { - return false; - } - } - - /** - * Returns the OpenemsWebsocket for the given device - * - * @param name - * @return - */ - public Optional getOpenemsWebsocket(String deviceName) { - Optional sessionOpt = this.sessionManager.getSessionByDeviceName(deviceName); - if (!sessionOpt.isPresent()) { - return Optional.empty(); - } - OpenemsSession session = sessionOpt.get(); - return this.getWebsocketFromSession(session); - } - - public Collection getSessions() { - return Collections.synchronizedCollection(this.sessionManager.getSessions()); - } - - public void setMetadataService(MetadataService metadataService) { - this.metadataService = metadataService; - } - - public void setBrowserWebsocketService(BrowserWebsocketService browserWebsocketService) { - this.browserWebsocketService = browserWebsocketService; - } - - public void setTimedataService(TimedataService timedataService) { - this.timedataService = timedataService; - } -} \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java b/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java deleted file mode 100644 index ac359d6e939..00000000000 --- a/io.openems.backend.openemswebsocket.provider/src/io/openems/backend/openemswebsocket/provider/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.openemswebsocket.provider; diff --git a/io.openems.backend.timedata.influx.provider/bnd.bnd b/io.openems.backend.timedata.influx.provider/bnd.bnd index a511cc64f61..767f0b75052 100644 --- a/io.openems.backend.timedata.influx.provider/bnd.bnd +++ b/io.openems.backend.timedata.influx.provider/bnd.bnd @@ -24,4 +24,4 @@ Private-Package: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 -Export-Package: io.openems.backend.timedata.api \ No newline at end of file +Export-Package: io.openems.backend.timedata.api;-provide=true \ No newline at end of file From 5ecc8e0ee96b92754f135cb04db325c5eff3289a Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 1 Feb 2018 16:31:07 +0100 Subject: [PATCH 025/156] Rename package to browserwebsocket.impl.provider. Enable startup of browserwebsocket bundle --- io.openems.backend.application/bnd.bnd | 3 +- .../io.openems.backend.application.bndrun | 3 +- .../backend/application/BackendApp.java | 4 +++ .../backend/application/BackendAppTest.java | 6 ---- .../.classpath | 8 +++++ .../.gitignore | 3 ++ .../.project | 23 ++++++++++++ .../org.eclipse.core.resources.prefs | 6 ++++ .../.settings/org.eclipse.jdt.core.prefs | 11 ++++++ .../bnd.bnd | 21 +++++++++++ .../readme.md | 8 +++++ .../api/BrowserWebsocketService.java | 8 +++++ .../browserwebsocket/api/package-info.java | 2 ++ .../test/.gitignore | 0 .../.classpath | 8 +++++ .../.gitignore | 3 ++ .../.project | 23 ++++++++++++ .../org.eclipse.core.resources.prefs | 7 ++++ .../.settings/org.eclipse.jdt.core.prefs | 11 ++++++ .../bnd.bnd | 20 +++++++++++ .../debug.bndrun | 13 +++++++ ...kend.browserwebsocket.impl.provider.bndrun | 20 +++++++++++ .../readme.md | 8 +++++ .../impl/provider/BrowserWebsocket.java | 35 +++++++++++++++++++ .../impl/provider/package-info.java | 2 ++ .../impl/provider/ProviderImplTest.java | 26 ++++++++++++++ .../bnd.bnd | 4 --- 27 files changed, 274 insertions(+), 12 deletions(-) create mode 100644 io.openems.backend.browserwebsocket.api/.classpath create mode 100644 io.openems.backend.browserwebsocket.api/.gitignore create mode 100644 io.openems.backend.browserwebsocket.api/.project create mode 100644 io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.browserwebsocket.api/bnd.bnd create mode 100644 io.openems.backend.browserwebsocket.api/readme.md create mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java create mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java create mode 100644 io.openems.backend.browserwebsocket.api/test/.gitignore create mode 100644 io.openems.backend.browserwebsocket.impl.provider/.classpath create mode 100644 io.openems.backend.browserwebsocket.impl.provider/.gitignore create mode 100644 io.openems.backend.browserwebsocket.impl.provider/.project create mode 100644 io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.browserwebsocket.impl.provider/bnd.bnd create mode 100644 io.openems.backend.browserwebsocket.impl.provider/debug.bndrun create mode 100644 io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun create mode 100644 io.openems.backend.browserwebsocket.impl.provider/readme.md create mode 100644 io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java create mode 100644 io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java create mode 100644 io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index 3006e386f94..0fd562cfd88 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -15,7 +15,8 @@ Private-Package: \ io.openems.backend.metadata.api;version=latest,\ io.openems.common;version=latest,\ io.openems.backend.timedata.api;version=latest,\ - io.openems.backend.openemswebsocket.api;version=latest + io.openems.backend.openemswebsocket.api;version=latest,\ + io.openems.backend.browserwebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/io.openems.backend.application.bndrun index 05ec3444b0c..34ee53c4c06 100644 --- a/io.openems.backend.application/io.openems.backend.application.bndrun +++ b/io.openems.backend.application/io.openems.backend.application.bndrun @@ -38,4 +38,5 @@ JPM-Command: openems-backend org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - io.openems.backend.openemswebsocket.impl.provider;version=snapshot \ No newline at end of file + io.openems.backend.openemswebsocket.impl.provider;version=snapshot,\ + io.openems.backend.browserwebsocket.impl.provider;version=snapshot \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index f65daffaf92..34c1d0f1f90 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; import io.openems.backend.timedata.api.TimedataService; @@ -33,6 +34,9 @@ public class BackendApp { @Reference OpenemsWebsocketService openemsWebsocketService; + @Reference + BrowserWebsocketService browserWebsocketService; + @Activate void activate() { configureLogging(); diff --git a/io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java b/io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java index e19ef5d0f73..d655d9a31d3 100644 --- a/io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java +++ b/io.openems.backend.application/test/io/openems/backend/application/BackendAppTest.java @@ -1,11 +1,5 @@ package io.openems.backend.application; -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; - -import io.openems.backend.application.BackendApp; - /* * Example JUNit test case * diff --git a/io.openems.backend.browserwebsocket.api/.classpath b/io.openems.backend.browserwebsocket.api/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.browserwebsocket.api/.gitignore b/io.openems.backend.browserwebsocket.api/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.browserwebsocket.api/.project b/io.openems.backend.browserwebsocket.api/.project new file mode 100644 index 00000000000..6a7fcd32642 --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.browserwebsocket.api + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..26e809422ae --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java=UTF-8 +encoding//src/io/openems/backend/browserwebsocket/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.browserwebsocket.api/bnd.bnd b/io.openems.backend.browserwebsocket.api/bnd.bnd new file mode 100644 index 00000000000..89805b5f67f --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/bnd.bnd @@ -0,0 +1,21 @@ +# +# io.openems.backend.browserwebsocket.api DEFAULTS +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: \ + io.openems.backend.browserwebsocket.api + +Require-Capability: \ + compile-only + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1 + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.browserwebsocket.api/readme.md b/io.openems.backend.browserwebsocket.api/readme.md new file mode 100644 index 00000000000..109f3501a3b --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.browserwebsocket.api + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java new file mode 100644 index 00000000000..a7bf79d079c --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java @@ -0,0 +1,8 @@ +package io.openems.backend.browserwebsocket.api; + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface BrowserWebsocketService { + +} diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java new file mode 100644 index 00000000000..b1bf5e6e5e2 --- /dev/null +++ b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.browserwebsocket.api; diff --git a/io.openems.backend.browserwebsocket.api/test/.gitignore b/io.openems.backend.browserwebsocket.api/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.backend.browserwebsocket.impl.provider/.classpath b/io.openems.backend.browserwebsocket.impl.provider/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.browserwebsocket.impl.provider/.gitignore b/io.openems.backend.browserwebsocket.impl.provider/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.browserwebsocket.impl.provider/.project b/io.openems.backend.browserwebsocket.impl.provider/.project new file mode 100644 index 00000000000..559cee152d1 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.browserwebsocket.impl.provider + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..d164fdd2828 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java=UTF-8 +encoding//test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.browserwebsocket.impl.provider.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.browserwebsocket.impl.provider/bnd.bnd b/io.openems.backend.browserwebsocket.impl.provider/bnd.bnd new file mode 100644 index 00000000000..7f014c574b3 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/bnd.bnd @@ -0,0 +1,20 @@ +# +# io.openems.backend.browserwebsocket.impl.provider PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: io.openems.backend.browserwebsocket.api + +Private-Package: io.openems.backend.browserwebsocket.impl.provider + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.browserwebsocket.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + diff --git a/io.openems.backend.browserwebsocket.impl.provider/debug.bndrun b/io.openems.backend.browserwebsocket.impl.provider/debug.bndrun new file mode 100644 index 00000000000..c8ccb93dafa --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.browserwebsocket.impl.provider DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.browserwebsocket.impl.provider.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun b/io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun new file mode 100644 index 00000000000..918a8877244 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun @@ -0,0 +1,20 @@ +# +# io.openems.backend.browserwebsocket.impl.provider LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.browserwebsocket.impl.provider.launch +JPM-Command: provider + + +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.impl.provider)' + +-runbundles: \ + io.openems.backend.browserwebsocket.impl.provider;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + slf4j.api;version='[1.8.0,1.8.1)' diff --git a/io.openems.backend.browserwebsocket.impl.provider/readme.md b/io.openems.backend.browserwebsocket.impl.provider/readme.md new file mode 100644 index 00000000000..2a6a2a6965b --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.browserwebsocket.impl.provider Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java new file mode 100644 index 00000000000..db516794da9 --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java @@ -0,0 +1,35 @@ +package io.openems.backend.browserwebsocket.impl.provider; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; + +import org.osgi.service.metatype.annotations.Designate; + + +@Designate( ocd=BrowserWebsocket.Config.class, factory=true) +@Component(name="BrowserWebsocket") +public class BrowserWebsocket implements BrowserWebsocketService { + + private final Logger log = LoggerFactory.getLogger(BrowserWebsocket.class); + + @ObjectClassDefinition + @interface Config { + int port(); + } + @Activate + void activate(Config config) { + log.debug("Activate BrowserWebsocket"); + } + + @Deactivate + void deactivate() { + log.debug("Deactivate BrowserWebsocket"); + } + +} diff --git a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java new file mode 100644 index 00000000000..b3435d7102d --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.browserwebsocket.impl.provider; diff --git a/io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java b/io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java new file mode 100644 index 00000000000..834eab1936e --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java @@ -0,0 +1,26 @@ +package io.openems.backend.browserwebsocket.impl.provider; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import io.openems.backend.browserwebsocket.impl.provider.BrowserWebsocket; + +/* + * Example JUNit test case + * + */ + +public class ProviderImplTest { + + /* + * Example test method + */ + + @Test + public void simple() { + BrowserWebsocket impl = new BrowserWebsocket(); + assertNotNull(impl); + } + +} diff --git a/io.openems.backend.openemswebsocket.api/bnd.bnd b/io.openems.backend.openemswebsocket.api/bnd.bnd index 5a0fc623572..bdd3a655518 100644 --- a/io.openems.backend.openemswebsocket.api/bnd.bnd +++ b/io.openems.backend.openemswebsocket.api/bnd.bnd @@ -3,10 +3,6 @@ # Bundle-Version: 1.0.0.${tstamp} -Bundle-Description: \ - This project contains a complete example with API, provider, and JUnit test code. \ - \ - ${warning;Please update this Bundle-Description in bnd.bnd} Export-Package: \ io.openems.backend.openemswebsocket.api From ef16dc458d36a242f85c49528aac9f177695dcd8 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 2 Feb 2018 11:25:52 +0100 Subject: [PATCH 026/156] Improve debug output --- .../.settings/org.eclipse.core.resources.prefs | 2 +- ...nems.backend.application.bndrun => BackendApp.bndrun} | 0 .../browserwebsocket/impl/provider/BrowserWebsocket.java | 2 +- .../src/io/openems/backend/metadata/odoo/Odoo.java | 9 +++++---- .../openemswebsocket/impl/provider/OpenemsWebsocket.java | 6 +++--- .../src/io/openems/backend/timedata/influx/Influx.java | 6 ++++-- 6 files changed, 14 insertions(+), 11 deletions(-) rename io.openems.backend.application/{io.openems.backend.application.bndrun => BackendApp.bndrun} (100%) diff --git a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs index 24ef32c8232..5901b50bf09 100644 --- a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,6 @@ eclipse.preferences.version=1 encoding//src/io/openems/backend/application/BackendApp.java=UTF-8 encoding//test/io/openems/backend/application/BackendAppTest.java=UTF-8 +encoding/BackendApp.bndrun=UTF-8 encoding/bnd.bnd=UTF-8 -encoding/io.openems.backend.application.bndrun=UTF-8 encoding/readme.md=UTF-8 diff --git a/io.openems.backend.application/io.openems.backend.application.bndrun b/io.openems.backend.application/BackendApp.bndrun similarity index 100% rename from io.openems.backend.application/io.openems.backend.application.bndrun rename to io.openems.backend.application/BackendApp.bndrun diff --git a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java index db516794da9..44bceed8209 100644 --- a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java +++ b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java @@ -24,7 +24,7 @@ public class BrowserWebsocket implements BrowserWebsocketService { } @Activate void activate(Config config) { - log.debug("Activate BrowserWebsocket"); + log.debug("Activate BrowserWebsocket [port=" + config.port() + "]"); } @Deactivate diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index ed4b83cc7c5..d1e5d627f44 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -36,12 +36,12 @@ public class Odoo implements MetadataService { private final Logger log = LoggerFactory.getLogger(Odoo.class); - + @ObjectClassDefinition @interface Config { String database(); - int uid(); + String uid(); String password(); @@ -50,12 +50,13 @@ public class Odoo implements MetadataService { private String url; private String database; - private int uid; + private String uid; private String password; @Activate void activate(Config config) { - log.debug("Activate Odoo"); + log.debug("Activate Odoo [url=" + config.url() + ";database=" + config.database() + ";uid=" + config.uid() + + ";password=" + (config.password() != null ? "ok" : "NOT_SET") + "]"); this.url = config.url(); this.database = config.database(); this.uid = config.uid(); diff --git a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java index 5976eecb3dc..6cc47118eb6 100644 --- a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java +++ b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java @@ -16,15 +16,15 @@ public class OpenemsWebsocket implements OpenemsWebsocketService { private final Logger log = LoggerFactory.getLogger(OpenemsWebsocket.class); - + @ObjectClassDefinition @interface Config { int port(); } - + @Activate void activate(Config config) { - log.debug("Activate OpenemsWebsocket"); + log.debug("Activate OpenemsWebsocket [port=" + config.port() + "]"); } @Deactivate diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index 07346ec00aa..fccb189543e 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -40,7 +40,7 @@ public class Influx implements TimedataService { private final Logger log = LoggerFactory.getLogger(Influx.class); - + private final String TMP_MINI_MEASUREMENT = "minies"; @ObjectClassDefinition @@ -71,7 +71,9 @@ public class Influx implements TimedataService { @Activate void activate(Config config) throws OpenemsException { - log.debug("Activate InfluxDB"); + log.debug("Activate InfluxDB [url=" + config.url() + ";port=" + config.port() + ";database=" + config.database() + + ";username=" + config.username() + ";password=" + (config.password() != null ? "ok" : "NOT_SET") + + ";measurement=" + config.measurement() + "]"); this.database = config.database(); this.url = config.url(); this.port = config.port(); From f3f5bfb9ade374070fca546a1365503ad84c1ba6 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Feb 2018 14:54:51 +0100 Subject: [PATCH 027/156] Update angular-cli to 1.6.7 + npm dependencies --- ui/.gitignore | 1 + ui/package-lock.json | 10267 ++++++++++++++++++++---------------- ui/package.json | 71 +- ui/src/tsconfig.spec.json | 1 - ui/tslint.json | 1 - ui/yarn.lock | 2751 +++++++--- 6 files changed, 7918 insertions(+), 5174 deletions(-) diff --git a/ui/.gitignore b/ui/.gitignore index 4d1be65dc15..5747e84395d 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -2,6 +2,7 @@ # compiled output /dist +/dist-server /tmp /out-tsc diff --git a/ui/package-lock.json b/ui/package-lock.json index 93e0e809964..c281cdb6308 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -5,13 +5,13 @@ "requires": true, "dependencies": { "@angular-devkit/build-optimizer": { - "version": "0.0.35", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.35.tgz", - "integrity": "sha512-7JxZZAYFSCc0tP6+NrRn3b2Cd1b9d+a3+OfwVNyNsNd2unelqUMko2hm0KLbC8BXcXt/OILg1E/ZgLAXSS47nw==", + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.42.tgz", + "integrity": "sha512-BAYCVZ10ro6mgZQDZiNiVbX8ppygw4q7z/stpwG8WjMswgMRIcxsxYoC1VFuWcUPAf4UyfTIav6e8UZWA5+xnQ==", "dev": true, "requires": { "loader-utils": "1.1.0", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "source-map": "0.5.7", "typescript": "2.6.2", "webpack-sources": "1.1.0" }, @@ -25,242 +25,125 @@ } }, "@angular-devkit/core": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.22.tgz", - "integrity": "sha512-zxrNtTiv60liye/GGeRMnnGgLgAWoqlMTfPLMW0D1qJ4bbrPHtme010mpxS3QL4edcDtQseyXSFCnEkuo2MrRw==", + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.29.tgz", + "integrity": "sha512-jtUBA0pIrkdXcVqDmDrGlniqwM7NFOKdo7vWFDmCVLBbC9rZHeYW5Xv/+4HyBhGLJ4wxsAkUjsHKWGJINPPpiw==", "dev": true, "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "ajv": "5.5.2", + "chokidar": "1.7.0", + "rxjs": "5.5.6", + "source-map": "0.5.7" + }, + "dependencies": { + "rxjs": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", + "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + } } }, "@angular-devkit/schematics": { - "version": "0.0.41", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.41.tgz", - "integrity": "sha512-eSXyRLM7g9NvNUwDd71iPjHEL0Zutg9PcLUSCrwFXR3Z8S6iStO2FpZACNmz5/Y7ksWLy5/1wjLuDJCHS4X/ig==", + "version": "0.0.52", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.52.tgz", + "integrity": "sha512-NtG8VB5aWtg0cw1Y7EJinJMuAnXsNdkQkkVe/i7CO6TPLyFQSFQCN1YojCr43l8jTWTRebRslrBawPCMOxsOgw==", "dev": true, "requires": { - "@angular-devkit/core": "0.0.22", "@ngtools/json-schema": "1.1.0", - "@schematics/schematics": "0.0.10", - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "rxjs": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz" - } - }, - "@angular/animations": { - "version": "https://registry.npmjs.org/@angular/animations/-/animations-5.0.0.tgz", - "integrity": "sha1-ta0ZnGf5P3WVREd+/+ZnnhVJkfs=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/cdk": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.0.0.tgz", - "integrity": "sha512-+u67Bns23tuCUGUCWhqkR/+onCwB4ObRURUv7ar2+BHw5VIvzML+IOCi1BJRF7gqvL+IVYQpLuY2cQh0J2SbBQ==", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" + "rxjs": "5.5.6" + }, + "dependencies": { + "rxjs": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", + "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + } } }, "@angular/cli": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.6.0.tgz", - "integrity": "sha512-X049QAgSKy5J48byyo3T6iBTHubTC66QBMshMpTT7PWAaSq3HpPPE1xW0WwgFIXZWMQIsmncaqPwmnFmxljUdw==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.6.7.tgz", + "integrity": "sha512-TprSjnQrEdrTsCAB5K/lCLuXZUH/y+l/BAR0aZLpubpZP8Ldgmq7q56trxL5wNSs3o6A8Vh43ZKNYOuKtnzlXQ==", "dev": true, "requires": { - "@angular-devkit/build-optimizer": "0.0.35", - "@angular-devkit/schematics": "0.0.41", + "@angular-devkit/build-optimizer": "0.0.42", + "@angular-devkit/core": "0.0.29", + "@angular-devkit/schematics": "0.0.52", "@ngtools/json-schema": "1.1.0", - "@ngtools/webpack": "1.9.0", - "@schematics/angular": "0.1.10", - "autoprefixer": "6.7.7", + "@ngtools/webpack": "1.9.7", + "@schematics/angular": "0.1.17", + "autoprefixer": "7.2.5", "chalk": "2.2.2", - "circular-dependency-plugin": "4.3.0", - "common-tags": "1.5.1", - "copy-webpack-plugin": "4.2.3", + "circular-dependency-plugin": "4.4.0", + "common-tags": "1.7.2", + "copy-webpack-plugin": "4.3.1", "core-object": "3.1.5", - "css-loader": "0.28.7", + "css-loader": "0.28.9", "cssnano": "3.10.0", "denodeify": "1.2.1", "ember-cli-string-utils": "1.1.0", "exports-loader": "0.6.4", "extract-text-webpack-plugin": "3.0.2", - "file-loader": "1.1.5", + "file-loader": "1.1.6", "fs-extra": "4.0.3", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "glob": "7.1.2", "html-webpack-plugin": "2.30.1", - "istanbul-instrumenter-loader": "2.0.0", + "istanbul-instrumenter-loader": "3.0.0", "karma-source-map-support": "1.2.0", "less": "2.7.3", "less-loader": "4.0.5", "license-webpack-plugin": "1.1.1", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "loader-utils": "1.1.0", + "lodash": "4.17.5", "memory-fs": "0.4.1", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "minimatch": "3.0.4", "node-modules-path": "1.0.1", "node-sass": "4.7.2", "nopt": "4.0.1", "opn": "5.1.0", "portfinder": "1.0.13", - "postcss-custom-properties": "6.2.0", - "postcss-loader": "2.0.9", + "postcss-import": "11.0.0", + "postcss-loader": "2.1.0", "postcss-url": "7.3.0", "raw-loader": "0.5.1", - "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", - "rxjs": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz", + "resolve": "1.5.0", + "rxjs": "5.5.6", "sass-loader": "6.0.6", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "semver": "5.5.0", "silent-error": "1.1.0", - "source-map-loader": "0.2.3", - "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "source-map-support": "0.4.18", "style-loader": "0.13.2", "stylus": "0.54.5", "stylus-loader": "3.0.1", - "uglifyjs-webpack-plugin": "1.1.2", + "uglifyjs-webpack-plugin": "1.1.8", "url-loader": "0.6.2", "webpack": "3.10.0", - "webpack-concat-plugin": "1.4.2", "webpack-dev-middleware": "1.12.2", - "webpack-dev-server": "2.9.7", + "webpack-dev-server": "2.11.1", "webpack-merge": "4.1.1", "webpack-sources": "1.1.0", - "webpack-subresource-integrity": "1.0.3", - "zone.js": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.18.tgz" + "webpack-subresource-integrity": "1.0.3" }, "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } - }, - "chalk": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", - "integrity": "sha1-RAP1zxjzXAX1H73xUr9Yj5Vs98s=", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "rxjs": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", + "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", "dev": true, "requires": { - "has-flag": "2.0.0" + "symbol-observable": "1.0.1" } } } }, - "@angular/common": { - "version": "https://registry.npmjs.org/@angular/common/-/common-5.0.0.tgz", - "integrity": "sha1-+W1mpRe5ldG6mygwnxXC41lnWCU=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/compiler": { - "version": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.0.0.tgz", - "integrity": "sha1-uf+/GMijnYt9rOxHMZOpDiTMK8k=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/compiler-cli": { - "version": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.0.0.tgz", - "integrity": "sha1-Dsu5N9hKT43ZTwwqR7B9LkaUyFM=", - "dev": true, - "requires": { - "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "reflect-metadata": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", - "tsickle": "https://registry.npmjs.org/tsickle/-/tsickle-0.24.1.tgz" - } - }, - "@angular/core": { - "version": "https://registry.npmjs.org/@angular/core/-/core-5.0.0.tgz", - "integrity": "sha1-T5dqIl993fNJkvLK2CTJVDpG9Mg=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/flex-layout": { - "version": "2.0.0-beta.10-4905443", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.10-4905443.tgz", - "integrity": "sha512-jjr6mQ3X2vdEQbsyHD/mz1hfTBUUEOZVLFWEz/sbNoeU7uiA4lvqdp/ASrkZydGJHmTDUYrbBE/9kx0lherZ8Q==", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/forms": { - "version": "https://registry.npmjs.org/@angular/forms/-/forms-5.0.0.tgz", - "integrity": "sha1-x/3fo1OWdZrphSkgowzdqMQe0d4=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/http": { - "version": "https://registry.npmjs.org/@angular/http/-/http-5.0.0.tgz", - "integrity": "sha1-Byiivgz7sHhyfF64fUyF1T/smlE=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/language-service": { - "version": "https://registry.npmjs.org/@angular/language-service/-/language-service-5.0.0.tgz", - "integrity": "sha1-bMu2n0dXJw3QTsWFfTdjSy8CxAw=", - "dev": true - }, - "@angular/material": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.0.0.tgz", - "integrity": "sha512-rzF061r4aYgaf2WI12Jk0+Rd4c1VBObdeMDFWRa3XKIcvETtkz+DXFH1n+vIC4YabfPgrdJRPrzdrZ7fKhRz6g==", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/platform-browser": { - "version": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.0.0.tgz", - "integrity": "sha1-xwOPfN6AcFtiAUiXIx4YLuyXb+0=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/platform-browser-dynamic": { - "version": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.0.0.tgz", - "integrity": "sha1-iH4QbIsQOwQVz2FWpCXabYP0yJ0=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, - "@angular/platform-server": { - "version": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-5.0.0.tgz", - "integrity": "sha1-h30l74FK+S//x7C1I7lxpv8iIBg=", - "requires": { - "domino": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz", - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", - "xhr2": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz" - } - }, - "@angular/router": { - "version": "https://registry.npmjs.org/@angular/router/-/router-5.0.0.tgz", - "integrity": "sha1-/ktSGmc4QIvOMPk6U0mRQMk6T3Y=", - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, "@ngtools/json-schema": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.1.0.tgz", @@ -268,332 +151,42 @@ "dev": true }, "@ngtools/webpack": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.9.0.tgz", - "integrity": "sha512-+9EFOELj9D7zarsKTiIFSbTL2Pr3jYdRJ/cEuSoTxzZUDq7f3urq9ILbcy3DTaDlZs2CCYKwgz+In8QnDtw4fg==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.9.7.tgz", + "integrity": "sha512-D5QuaT9wENeM2j9g2qvW9Ls1tGqRz26Lp+jxwb2ZGFep7Ik1fFOX3ROLfgkxNlxZGVmbxJjsfrYUCyGlzj8gWg==", "dev": true, "requires": { "chalk": "2.2.2", "enhanced-resolve": "3.4.1", "loader-utils": "1.1.0", "magic-string": "0.22.4", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "tree-kill": "1.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } - }, - "chalk": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", - "integrity": "sha1-RAP1zxjzXAX1H73xUr9Yj5Vs98s=", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } + "semver": "5.5.0", + "source-map": "0.5.7", + "tree-kill": "1.2.0", + "webpack-sources": "1.1.0" } }, - "@ngx-translate/core": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-9.0.1.tgz", - "integrity": "sha1-AA8thjxMlMgY4UFu9DzKLFwMWEg=" - }, "@schematics/angular": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.1.10.tgz", - "integrity": "sha512-ykq4FL0WTygkpvIcGDxnxHHT2uvJMWseDeAujmfyZpzdT9X22GOTURNo3LjvOIhhVUpMVZvnAYqjV46KqB702g==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.0.22" - } - }, - "@schematics/schematics": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@schematics/schematics/-/schematics-0.0.10.tgz", - "integrity": "sha512-9vr9W1X6oRp42pbiGRIk3L+T6SoFtHlAGrzbh6rbFQDNXT4UCHarqDigow+DEL6PR2ptXZO9WeLcad4it7zNyA==", - "dev": true - }, - "@types/d3": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-4.12.0.tgz", - "integrity": "sha512-F5lVj6c2G/WPbKFk4ZVxTS8F/6IRknWcheswQcycMjBh17iJ+bRfNUtn0yvtIHtRPQSanI9Dx2U8rSSA/I+ecQ==", - "requires": { - "@types/d3-array": "1.2.1", - "@types/d3-axis": "1.0.9", - "@types/d3-brush": "1.0.7", - "@types/d3-chord": "1.0.6", - "@types/d3-collection": "1.0.5", - "@types/d3-color": "1.0.5", - "@types/d3-dispatch": "1.0.5", - "@types/d3-drag": "1.2.0", - "@types/d3-dsv": "1.0.31", - "@types/d3-ease": "1.0.7", - "@types/d3-force": "1.1.0", - "@types/d3-format": "1.2.1", - "@types/d3-geo": "1.9.4", - "@types/d3-hierarchy": "1.1.0", - "@types/d3-interpolate": "1.1.6", - "@types/d3-path": "1.0.6", - "@types/d3-polygon": "1.0.5", - "@types/d3-quadtree": "1.0.5", - "@types/d3-queue": "3.0.5", - "@types/d3-random": "1.1.0", - "@types/d3-request": "1.0.2", - "@types/d3-scale": "1.0.10", - "@types/d3-selection": "1.2.0", - "@types/d3-shape": "1.2.1", - "@types/d3-time": "1.0.7", - "@types/d3-time-format": "2.1.0", - "@types/d3-timer": "1.0.6", - "@types/d3-transition": "1.1.1", - "@types/d3-voronoi": "1.1.7", - "@types/d3-zoom": "1.7.0" - } - }, - "@types/d3-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.1.tgz", - "integrity": "sha1-5IlgUgjUahydmA0uV3L6nHXZ7GU=" - }, - "@types/d3-axis": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.9.tgz", - "integrity": "sha1-Ys57yNBDVCmM2lfz8dH4Vq1puJo=", - "requires": { - "@types/d3-selection": "1.2.0" - } - }, - "@types/d3-brush": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.0.7.tgz", - "integrity": "sha1-BcMEQPTVN/0j+Xaw5sS6IjAB70U=", - "requires": { - "@types/d3-selection": "1.2.0" - } - }, - "@types/d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha1-BYnrl6MZH07a8Xt73kmEYokM4ew=" - }, - "@types/d3-collection": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.5.tgz", - "integrity": "sha1-ux86qXzcjYgWRVQbnWz4ft/um8M=" - }, - "@types/d3-color": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.0.5.tgz", - "integrity": "sha1-ytdV8Pxt57cPpuXgivqB70wiSN4=" - }, - "@types/d3-dispatch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.5.tgz", - "integrity": "sha1-8fkYe1OOywUVdWnY3C9w37BPG1I=" - }, - "@types/d3-drag": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.0.tgz", - "integrity": "sha1-XuYnlDLIlPhcty/NqRGpWbrhGVI=", - "requires": { - "@types/d3-selection": "1.2.0" - } - }, - "@types/d3-dsv": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.31.tgz", - "integrity": "sha512-UCAVZdwd2NkrbkF1lZu9vzTlmUENRRrPCubyhDPlG8Ye1B8Xr2PNvk/Tp8tMm6sPoWZWagri6/P9H+t7WqkGDg==" - }, - "@types/d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha1-k6MBhovp4VBh89RDQ7GrP4rLbwk=" - }, - "@types/d3-force": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.1.0.tgz", - "integrity": "sha1-QJJco1ErY71CT3yWheF4G1sKHX4=" - }, - "@types/d3-format": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.2.1.tgz", - "integrity": "sha1-lDX7F3HS+/aoWMkyGPQJfJqjlsE=" - }, - "@types/d3-geo": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.9.4.tgz", - "integrity": "sha512-DoigJorMGGIG9K4n980zz5g1XnvhDhNy7rk/0O8KCpFPpUZ9hyAgN0ZHXhbtIelxhJhMZxwMRe2soxx/Fhx4Hg==", - "requires": { - "@types/geojson": "7946.0.0" - } - }, - "@types/d3-hierarchy": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.0.tgz", - "integrity": "sha1-UPHuBShAY4A1y91KyrH8NHCQWQc=" - }, - "@types/d3-interpolate": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.1.6.tgz", - "integrity": "sha1-ZAQbFcnAMsNI2hsiuqvFn6TRYTY=", - "requires": { - "@types/d3-color": "1.0.5" - } - }, - "@types/d3-path": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.6.tgz", - "integrity": "sha1-wafS3Aeylf3RyE2r5EBN+ZG0hpM=" - }, - "@types/d3-polygon": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.5.tgz", - "integrity": "sha1-Na1U7YTDnX6fElK2U1vmAL5srOI=" - }, - "@types/d3-quadtree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.5.tgz", - "integrity": "sha1-HOHmWerkUw3wyxJ/KX8XQaNnqC4=" - }, - "@types/d3-queue": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-queue/-/d3-queue-3.0.5.tgz", - "integrity": "sha1-Pky+Kv9h22oLK4xIACmeTsasyFA=" - }, - "@types/d3-random": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.0.tgz", - "integrity": "sha1-LdCPEVnHBxknDkp8g0r4XIuI0sM=" - }, - "@types/d3-request": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-request/-/d3-request-1.0.2.tgz", - "integrity": "sha1-2524FU9HgWWEcGxub3Ar5m8i9L4=", - "requires": { - "@types/d3-dsv": "1.0.31" - } - }, - "@types/d3-scale": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-1.0.10.tgz", - "integrity": "sha1-jFwdylShWe7QQrRnGduzvbfoyEI=", - "requires": { - "@types/d3-time": "1.0.7" - } - }, - "@types/d3-selection": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.2.0.tgz", - "integrity": "sha512-QPmtQnzJA8dtKEd1+FbtirI6ENncf52I754Jidnvnozo+bqJpl3oaZADth7gDamQWx0bJmFrajvxkPxfh88+Ow==" - }, - "@types/d3-shape": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.2.1.tgz", - "integrity": "sha1-ysLZ8BIvFzIgwyyMFS3ELuk0nfI=", - "requires": { - "@types/d3-path": "1.0.6" - } - }, - "@types/d3-time": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.7.tgz", - "integrity": "sha1-QmbXyb4V+oElaojR0FLWHNjcVyw=" - }, - "@types/d3-time-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", - "integrity": "sha1-AR4Pt5N740qaj1gK4eLy8TNqiiI=" - }, - "@types/d3-timer": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.6.tgz", - "integrity": "sha1-eG1OIHMa3wOvLF32yG/ilmf+Qps=" - }, - "@types/d3-transition": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.1.tgz", - "integrity": "sha1-wgn85qlm1mljVt1CsJGpxsx5kp8=", - "requires": { - "@types/d3-selection": "1.2.0" - } - }, - "@types/d3-voronoi": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.7.tgz", - "integrity": "sha1-wKFFzwQ5WSfgFwb/bE/4Ncl6js4=" - }, - "@types/d3-zoom": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.0.tgz", - "integrity": "sha1-EiG79kNIIPBEyAtVHFUZuBcAiWE=", - "requires": { - "@types/d3-interpolate": "1.1.6", - "@types/d3-selection": "1.2.0" - } - }, - "@types/geojson": { - "version": "7946.0.0", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.0.tgz", - "integrity": "sha512-lbWmXFxIpEzpH7OprsCRvxj7kie+248Y2ItjeVsF+0+IqvwG+R+0xgZmxq1ofYNTszvuihDahas7O5dscfxTsw==" - }, - "@types/jasmine": { - "version": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.54.tgz", - "integrity": "sha1-prXyrir7bgMHd06MfGCOA31JHGM=", - "dev": true - }, - "@types/jasminewd2": { - "version": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz", - "integrity": "sha1-DSiGsMva5MDuulXjB5L1hL8ECpU=", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.1.17.tgz", + "integrity": "sha512-PHE5gk/ogPY/aN94dbbtauHMCq+/7w4Kdcl7tGmSS8mPKEI0wa6XJi//Wq/tHi55lb2fP58oEZU6n6w/wQascw==", "dev": true, "requires": { - "@types/jasmine": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.54.tgz" + "typescript": "2.6.2" + }, + "dependencies": { + "typescript": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "dev": true + } } }, - "@types/node": { - "version": "https://registry.npmjs.org/@types/node/-/node-6.0.89.tgz", - "integrity": "sha1-FUvg5qgjdgzWCDqoxI+VLi5j4LA=", - "dev": true - }, - "@types/q": { - "version": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz", - "integrity": "sha1-dMt3+2BS7a/yqJhN2v2I1BnyXKw=", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "accepts": { @@ -602,14 +195,14 @@ "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", "dev": true, "requires": { - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" + "mime-types": "2.1.17", + "negotiator": "0.6.1" } }, "acorn": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", - "integrity": "sha1-MXrHghgmwixwLWYYmrg1lnXxNdc=", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", + "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", "dev": true }, "acorn-dynamic-import": { @@ -629,41 +222,16 @@ } } }, - "adm-zip": { - "version": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=", - "dev": true - }, - "after": { - "version": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", - "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", - "dev": true, - "requires": { - "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "semver": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz" - }, - "dependencies": { - "semver": { - "version": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", - "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", - "dev": true - } - } - }, "ajv": { - "version": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", - "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "fast-deep-equal": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "json-schema-traverse": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "ajv-keywords": { @@ -673,13 +241,14 @@ "dev": true }, "align-text": { - "version": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "longest": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "alphanum-sort": { @@ -689,19 +258,11 @@ "dev": true }, "amdefine": { - "version": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "angular2-toaster": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/angular2-toaster/-/angular2-toaster-4.0.1.tgz", - "integrity": "sha1-WHrvbfkDz14rfrpAR7VOyaIbjjs=" - }, - "angular2-uuid": { - "version": "https://registry.npmjs.org/angular2-uuid/-/angular2-uuid-1.1.1.tgz", - "integrity": "sha1-cvA81TK39AAy6x7PufhFc4S+lW4=" - }, "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", @@ -709,42 +270,34 @@ "dev": true }, "ansi-regex": { - "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { - "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + "color-convert": "1.9.1" } }, - "app-root-path": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", - "integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=", - "dev": true - }, - "append-transform": { - "version": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "default-require-extensions": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" } }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "are-we-there-yet": { @@ -754,28 +307,37 @@ "dev": true, "requires": { "delegates": "1.0.0", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "readable-stream": "2.3.3" } }, "argparse": { - "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { - "sprintf-js": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + "sprintf-js": "1.0.3" } }, "arr-diff": { - "version": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" + "arr-flatten": "1.1.0" } }, "arr-flatten": { - "version": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, "array-find-index": { @@ -800,36 +362,30 @@ "es-abstract": "1.10.0" } }, - "array-slice": { - "version": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, "array-union": { - "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" + "array-uniq": "1.0.3" } }, "array-uniq": { - "version": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, "array-unique": { - "version": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, - "arraybuffer.slice": { - "version": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", - "dev": true - }, "arrify": { - "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, @@ -841,18 +397,19 @@ "optional": true }, "asn1": { - "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", "dev": true }, "asn1.js": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", - "integrity": "sha1-gRfvT37YfNj4kES1v/l6wkOhbJo=", + "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", "dev": true, "requires": { "bn.js": "4.11.8", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "minimalistic-assert": "1.0.0" } }, @@ -866,20 +423,29 @@ } }, "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, "async": { - "version": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", "dev": true, "requires": { - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "async-each": { - "version": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, @@ -891,177 +457,213 @@ "optional": true }, "asynckit": { - "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "atob": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", + "dev": true + }, "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.5.tgz", + "integrity": "sha512-XqHfo8Ht0VU+T5P+eWEVoXza456KJ4l62BPewu3vpNf3LP9s2+zYXkXBznzYby4XeECXgG3N4i+hGvOhXErZmA==", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000782", + "browserslist": "2.11.3", + "caniuse-lite": "1.0.30000803", "normalize-range": "0.1.2", "num2fraction": "1.2.2", - "postcss": "5.2.18", + "postcss": "6.0.17", "postcss-value-parser": "3.3.0" } }, "aws-sign2": { - "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", "dev": true }, "aws4": { - "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", "dev": true }, "babel-code-frame": { - "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" }, "dependencies": { - "chalk": { - "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "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=", "dev": true, "requires": { - "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } } }, "babel-generator": { - "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", - "dev": true, - "requires": { - "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "detect-indent": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "trim-right": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.5", + "source-map": "0.5.7", + "trim-right": "1.0.1" }, "dependencies": { "jsesc": { - "version": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true } } }, "babel-messages": { - "version": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" + "babel-runtime": "6.26.0" } }, "babel-runtime": { - "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + "core-js": "2.5.3", + "regenerator-runtime": "0.11.1" }, "dependencies": { "core-js": { - "version": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", + "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=", "dev": true } } }, "babel-template": { - "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.5" } }, "babel-traverse": { - "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.5" } }, "babel-types": { - "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.5", + "to-fast-properties": "1.0.3" } }, "babylon": { - "version": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", - "dev": true - }, - "backo2": { - "version": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { - "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base64-arraybuffer": { - "version": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.0", + "pascalcase": "0.1.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } }, "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=", - "dev": true - }, - "base64id": { - "version": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", "dev": true }, "batch": { @@ -1071,36 +673,25 @@ "dev": true }, "bcrypt-pbkdf": { - "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, "optional": true, "requires": { - "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - } - }, - "better-assert": { - "version": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz" + "tweetnacl": "0.14.5" } }, "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", "dev": true }, "binary-extensions": { - "version": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", - "dev": true - }, - "blob": { - "version": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, "block-stream": { @@ -1110,43 +701,45 @@ "dev": true, "optional": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - }, - "blocking-proxy": { - "version": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.5.tgz", - "integrity": "sha1-RikF4Nz76pcPQao3Ij3anAexkSs=", - "dev": true, - "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + "inherits": "2.0.3" } }, "bluebird": { - "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk=", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", "dev": true }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", "dev": true }, "body-parser": { - "version": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "dev": true, "requires": { - "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "raw-body": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz" + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + }, + "dependencies": { + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + } } }, "bonjour": { @@ -1159,7 +752,7 @@ "deep-equal": "1.0.1", "dns-equal": "1.0.0", "dns-txt": "2.0.2", - "multicast-dns": "6.2.1", + "multicast-dns": "6.2.3", "multicast-dns-service-types": "1.1.0" } }, @@ -1170,30 +763,33 @@ "dev": true }, "boom": { - "version": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + "hoek": "2.16.3" } }, "brace-expansion": { - "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { - "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "balanced-match": "1.0.0", + "concat-map": "0.0.1" } }, "braces": { - "version": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "preserve": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "brorand": { @@ -1205,15 +801,15 @@ "browserify-aes": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", - "integrity": "sha1-OLerVe24Bv8tzaGn8WIHc6R3xJ8=", + "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", "dev": true, "requires": { "buffer-xor": "1.0.3", "cipher-base": "1.0.4", "create-hash": "1.1.3", "evp_bytestokey": "1.0.3", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "browserify-cipher": { @@ -1235,7 +831,7 @@ "requires": { "cipher-base": "1.0.4", "des.js": "1.0.0", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "inherits": "2.0.3" } }, "browserify-rsa": { @@ -1245,7 +841,7 @@ "dev": true, "requires": { "bn.js": "4.11.8", - "randombytes": "2.0.5" + "randombytes": "2.0.6" } }, "browserify-sign": { @@ -1259,7 +855,7 @@ "create-hash": "1.1.3", "create-hmac": "1.1.6", "elliptic": "6.4.0", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "parse-asn1": "5.1.0" } }, @@ -1273,13 +869,13 @@ } }, "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", "dev": true, "requires": { - "caniuse-db": "1.0.30000782", - "electron-to-chromium": "1.3.28" + "caniuse-lite": "1.0.30000803", + "electron-to-chromium": "1.3.32" } }, "buffer": { @@ -1290,13 +886,13 @@ "requires": { "base64-js": "1.2.1", "ieee754": "1.1.8", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "isarray": "1.0.0" } }, "buffer-indexof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha1-Uvq8xqYG0aADAoAmSO9o9jnaJow=", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, "buffer-xor": { @@ -1318,35 +914,56 @@ "dev": true }, "bytes": { - "version": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, "cacache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.1.tgz", - "integrity": "sha512-dRHYcs9LvG9cHgdPzjiI+/eS7e1xRhULrcyOx04RZQsszNJXU2SL9CyG60yLnge282Qq5nwTv+ieK2fH+WPZmA==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.2.tgz", + "integrity": "sha512-dljb7dk1jqO5ogE+dRpoR9tpHYv5xz9vPSNunh1+0wRuNdYxmzp9WmsyokgW/DUF1FDRVA/TMsmxt027R8djbQ==", "dev": true, "requires": { - "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "bluebird": "3.5.1", "chownr": "1.0.1", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "glob": "7.1.2", + "graceful-fs": "4.1.11", "lru-cache": "4.1.1", - "mississippi": "1.3.0", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "mississippi": "1.3.1", + "mkdirp": "0.5.1", "move-concurrently": "1.0.1", "promise-inflight": "1.0.1", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "ssri": "5.0.0", + "rimraf": "2.6.2", + "ssri": "5.1.0", "unique-filename": "1.1.0", "y18n": "3.2.1" } }, - "callsite": { - "version": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } }, "camel-case": { "version": "3.0.0", @@ -1381,134 +998,86 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000782", + "caniuse-db": "1.0.30000803", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000803", + "electron-to-chromium": "1.3.32" + } + } } }, "caniuse-db": { - "version": "1.0.30000782", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000782.tgz", - "integrity": "sha1-2IFbzhV4w1Cs7REyUHMBIF4Pq1M=", + "version": "1.0.30000803", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000803.tgz", + "integrity": "sha1-Po0rr1bC/VpZyC4ieSig3CwmcC0=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30000803", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000803.tgz", + "integrity": "sha512-AoROHIFLv2iv5CG4nonOfT9ZCQ3JTN0GyEn8LG2sPb2Wc5cIyX/UwLYP0pnVajVF3LWH+mrO/DXBzmte0BK9cQ==", "dev": true }, "caseless": { - "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "dev": true, + "optional": true }, "center-align": { - "version": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "requires": { - "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "lazy-cache": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" }, "dependencies": { "lazy-cache": { - "version": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true } } }, "chalk": { - "version": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha1-rFvs8U+iG5nGySynp9fP1bF+dD4=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", + "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", "dev": true, "requires": { - "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz" - }, - "dependencies": { - "ansi-styles": { - "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } - }, - "has-flag": { - "version": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", - "dev": true, - "requires": { - "has-flag": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz" - } - } - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true - }, - "chart.js": { - "version": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.1.tgz", - "integrity": "sha1-rpC0qk/x8C3s1rGiqNq/1zyfmIY=", - "requires": { - "chartjs-color": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", - "moment": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz" - } - }, - "chartjs-color": { - "version": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", - "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", - "requires": { - "chartjs-color-string": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz" - }, - "dependencies": { - "color-convert": { - "version": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", - "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" - } - } - }, - "chartjs-color-string": { - "version": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", - "integrity": "sha1-jTdS2Fgdhmh8Nb/iy4CsUhPOuME=", - "requires": { - "color-name": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" } }, "chokidar": { - "version": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "async-each": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "is-binary-path": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "readdirp": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz" - }, - "dependencies": { - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" - } - } + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.1.3", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" } }, "chownr": { @@ -1520,38 +1089,44 @@ "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "circular-dependency-plugin": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-4.3.0.tgz", - "integrity": "sha512-L3W9L1S0wC64rq+QSaZzmWnJW7cVBgimxI2lNEFEX5biwlRG8EHRM68JFi+CX5ZkCGUWJHIpnhdVs181Zlq3wA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-4.4.0.tgz", + "integrity": "sha512-yEFtUNUYT4jBykEX5ZOHw+5goA3glGZr9wAXIQqoyakjz5H5TeUmScnWRc52douAhb9eYzK3s7V6bXfNnjFdzg==", "dev": true }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", "dev": true, "requires": { "chalk": "1.1.3" }, "dependencies": { + "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=", "dev": true, "requires": { - "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", "supports-color": "2.0.0" } }, @@ -1563,9 +1138,91 @@ } } }, - "classlist.js": { - "version": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz", - "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k=" + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } }, "clean-css": { "version": "4.1.9", @@ -1573,7 +1230,7 @@ "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=", "dev": true, "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "source-map": "0.5.7" } }, "cliui": { @@ -1583,7 +1240,7 @@ "dev": true, "requires": { "string-width": "1.0.2", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "strip-ansi": "3.0.1", "wrap-ansi": "2.1.0" } }, @@ -1601,12 +1258,24 @@ "requires": { "for-own": "1.0.0", "is-plain-object": "2.0.4", - "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "kind-of": "3.2.2", "shallow-clone": "0.1.2" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + } } }, "co": { - "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, @@ -1625,18 +1294,14 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, - "codelyzer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.0.1.tgz", - "integrity": "sha512-MsOcaiLqcBK7hjHbfp9HZrflqWg5tD9A5qVSXkW208OJ8pkf63id8IiOjEiK/XU3o70W8tWbFKi1tAOwiJDMrQ==", + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "app-root-path": "2.0.1", - "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "cssauron": "1.4.0", - "semver-dsl": "1.0.1", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "sprintf-js": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color": { @@ -1646,21 +1311,24 @@ "dev": true, "requires": { "clone": "1.0.3", - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "color-convert": "1.9.1", "color-string": "0.3.0" } }, "color-convert": { - "version": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "color-name": "1.1.3" } }, "color-name": { - "version": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "color-string": { "version": "0.3.0", @@ -1668,7 +1336,7 @@ "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "dev": true, "requires": { - "color-name": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "color-name": "1.1.3" } }, "colormin": { @@ -1683,37 +1351,33 @@ } }, "colors": { - "version": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, - "combine-lists": { - "version": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" - } - }, "combined-stream": { - "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, "requires": { - "delayed-stream": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "delayed-stream": "1.0.0" } }, "commander": { - "version": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=" + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", + "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", + "dev": true }, "common-tags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.5.1.tgz", - "integrity": "sha512-NrUYGY5TApAk9KB+IZXkR3GR4tA3g26HDsoiGt4kCMHZ727gOGkC+UNfq0Z22jE15bLkc/6RV5Jw1RBW6Usg6A==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz", + "integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==", "dev": true, "requires": { - "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" + "babel-runtime": "6.26.0" } }, "commondir": { @@ -1722,19 +1386,10 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "component-bind": { - "version": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, "component-emitter": { - "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, - "component-inherit": { - "version": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, "compressible": { @@ -1743,7 +1398,7 @@ "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", "dev": true, "requires": { - "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz" + "mime-db": "1.30.0" } }, "compression": { @@ -1753,16 +1408,17 @@ "dev": true, "requires": { "accepts": "1.3.4", - "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "bytes": "3.0.0", "compressible": "2.0.12", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "debug": "2.6.9", "on-headers": "1.0.1", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "safe-buffer": "5.1.1", "vary": "1.1.2" } }, "concat-map": { - "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, @@ -1772,38 +1428,11 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "inherits": "2.0.3", + "readable-stream": "2.3.3", "typedarray": "0.0.6" } }, - "connect": { - "version": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", - "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", - "dev": true, - "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "finalhandler": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", - "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "utils-merge": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - }, - "dependencies": { - "finalhandler": { - "version": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", - "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", - "dev": true, - "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" - } - } - } - }, "connect-history-api-fallback": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", @@ -1838,8 +1467,9 @@ "dev": true }, "content-type": { - "version": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "convert-source-map": { @@ -1849,7 +1479,8 @@ "dev": true }, "cookie": { - "version": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, @@ -1862,31 +1493,56 @@ "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { "aproba": "1.2.0", "fs-write-stream-atomic": "1.0.10", "iferr": "0.1.5", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", "run-queue": "1.0.3" } }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, "copy-webpack-plugin": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.2.3.tgz", - "integrity": "sha512-cL/Wl3Y1QmmKThl/mWeGB+HH3YH+25tn8nhqEGsZda4Yn7GqGnDZ+TbeKJ7A6zvrxyNhhuviYAxn/tCyyAqh8Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.3.1.tgz", + "integrity": "sha512-xlcFiW/U7KrpS6dFuWq3r8Wb7koJx7QVc7LDFCosqkikaVSxkaYOnwDLwilbjrszZ0LYZXThDAJKcQCSrvdShQ==", "dev": true, "requires": { - "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "cacache": "10.0.2", + "find-cache-dir": "1.0.0", + "globby": "7.1.1", "is-glob": "4.0.0", "loader-utils": "0.2.17", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "lodash": "4.17.5", + "minimatch": "3.0.4", + "p-limit": "1.2.0", + "pify": "3.0.0", + "serialize-javascript": "1.4.0" }, "dependencies": { + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, "loader-utils": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", @@ -1896,42 +1552,47 @@ "big.js": "3.2.0", "emojis-list": "2.1.0", "json5": "0.5.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "object-assign": "4.1.1" } } } }, - "core-js": { - "version": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" - }, "core-object": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/core-object/-/core-object-3.1.5.tgz", - "integrity": "sha1-+mJ7h1Aq3JgEXkRnjpqOw7nA0qk=", + "integrity": "sha512-sA2/4+/PZ/KV6CKgjrVrrUVBKCkdDO02CUlQ0YKTQoYUwPYNOtOAcWlbYhd5v/1JqYaA6oZ4sDlOU4ppVw6Wbg==", "dev": true, "requires": { - "chalk": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz" + "chalk": "2.2.2" } }, "core-util-is": { - "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "cosmiconfig": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "dev": true, "requires": { "is-directory": "0.3.1", - "js-yaml": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "js-yaml": "3.7.0", + "minimist": "1.2.0", + "object-assign": "4.1.1", "os-homedir": "1.0.2", "parse-json": "2.2.0", "require-from-string": "1.2.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "create-ecdh": { @@ -1951,9 +1612,9 @@ "dev": true, "requires": { "cipher-base": "1.0.4", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "ripemd160": "2.0.1", - "sha.js": "2.4.9" + "sha.js": "2.4.10" } }, "create-hmac": { @@ -1964,10 +1625,10 @@ "requires": { "cipher-base": "1.0.4", "create-hash": "1.1.3", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "ripemd160": "2.0.1", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "sha.js": "2.4.9" + "safe-buffer": "5.1.1", + "sha.js": "2.4.10" } }, "cross-spawn": { @@ -1978,31 +1639,16 @@ "optional": true, "requires": { "lru-cache": "4.1.1", - "which": "https://registry.npmjs.org/which/-/which-1.3.0.tgz" + "which": "1.3.0" } }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "dev": true - }, "cryptiles": { - "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { - "boom": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz" - }, - "dependencies": { - "boom": { - "version": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=", - "dev": true, - "requires": { - "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" - } - } + "boom": "2.10.1" } }, "crypto-browserify": { @@ -2017,10 +1663,10 @@ "create-hash": "1.1.3", "create-hmac": "1.1.6", "diffie-hellman": "5.0.2", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "pbkdf2": "3.0.14", "public-encrypt": "4.0.0", - "randombytes": "2.0.5", + "randombytes": "2.0.6", "randomfill": "1.0.3" } }, @@ -2031,72 +1677,121 @@ "dev": true }, "css-loader": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz", - "integrity": "sha1-Xy7pid0y7dkHcX+VMxdlYWCZnBs=", + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.9.tgz", + "integrity": "sha512-r3dgelMm/mkPz5Y7m9SeiGE46i2VsEU/OYbez+1llfxtv8b2y5/b5StaeEvPK3S5tlNQI+tDW/xDIhKJoZgDtw==", "dev": true, "requires": { - "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "babel-code-frame": "6.26.0", + "css-selector-tokenizer": "0.7.0", "cssnano": "3.10.0", "icss-utils": "2.1.0", "loader-utils": "1.1.0", "lodash.camelcase": "4.3.0", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "postcss": "5.2.18", - "postcss-modules-extract-imports": "1.1.0", + "postcss-modules-extract-imports": "1.2.0", "postcss-modules-local-by-default": "1.2.0", "postcss-modules-scope": "1.1.0", "postcss-modules-values": "1.3.0", "postcss-value-parser": "3.3.0", "source-list-map": "2.0.0" - } - }, - "css-parse": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", - "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", - "dev": true - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", - "domutils": "1.5.1", - "nth-check": "1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "fastparse": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "regexpu-core": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", "dev": true }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { - "through": "2.3.8" + "boolbase": "1.0.0", + "css-what": "2.1.0", + "domutils": "1.5.1", + "nth-check": "1.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "dev": true, + "requires": { + "cssesc": "0.1.0", + "fastparse": "1.1.1", + "regexpu-core": "1.0.0" } }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + "dev": true + }, "cssesc": { - "version": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", "dev": true }, @@ -2107,10 +1802,10 @@ "dev": true, "requires": { "autoprefixer": "6.7.7", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "decamelize": "1.2.0", "defined": "1.0.0", "has": "1.0.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "postcss": "5.2.18", "postcss-calc": "5.3.1", "postcss-colormin": "2.2.2", @@ -2138,6 +1833,86 @@ "postcss-unique-selectors": "2.0.2", "postcss-value-parser": "3.3.0", "postcss-zindex": "2.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000803", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000803", + "electron-to-chromium": "1.3.32" + } + }, + "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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "csso": { @@ -2147,7 +1922,7 @@ "dev": true, "requires": { "clap": "1.2.3", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "source-map": "0.5.7" } }, "cuint": { @@ -2165,11 +1940,6 @@ "array-find-index": "1.0.2" } }, - "custom-event": { - "version": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, "cyclist": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", @@ -2182,286 +1952,26 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.37" - } - }, - "d3": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-4.12.0.tgz", - "integrity": "sha512-ibAzFJrj1uuX+wIy29baTx9JebA5i670UKlp+H3c9pg0w8PKl8jpj4zf64MmLPVBXm2vA1i29obnWuM/rmyfWA==", - "requires": { - "d3-array": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "d3-axis": "1.0.8", - "d3-brush": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", - "d3-chord": "1.0.4", - "d3-collection": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "d3-color": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-drag": "1.2.1", - "d3-dsv": "1.0.8", - "d3-ease": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "d3-force": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", - "d3-format": "1.2.1", - "d3-geo": "1.9.0", - "d3-hierarchy": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", - "d3-interpolate": "1.1.6", - "d3-path": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", - "d3-polygon": "1.0.3", - "d3-quadtree": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", - "d3-queue": "3.0.7", - "d3-random": "1.1.0", - "d3-request": "1.0.6", - "d3-scale": "1.0.7", - "d3-selection": "1.2.0", - "d3-shape": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1", - "d3-timer": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", - "d3-transition": "1.1.1", - "d3-voronoi": "1.1.2", - "d3-zoom": "1.7.1" - }, - "dependencies": { - "d3-drag": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", - "integrity": "sha1-343UxQL7SQ/HRiBGqK2YpcR5KC0=", - "requires": { - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-selection": "1.2.0" - } - }, - "d3-transition": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", - "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", - "requires": { - "d3-color": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-ease": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "d3-interpolate": "1.1.6", - "d3-selection": "1.2.0", - "d3-timer": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz" - } - } - } - }, - "d3-array": { - "version": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "integrity": "sha1-0coz3i9qwx76244FCgIdfiOW1dw=" - }, - "d3-axis": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz", - "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=" - }, - "d3-brush": { - "version": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", - "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", - "requires": { - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-drag": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.1.1.tgz", - "d3-interpolate": "1.1.6", - "d3-selection": "1.2.0", - "d3-transition": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.0.tgz" - } - }, - "d3-chord": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", - "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", - "requires": { - "d3-array": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "d3-path": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz" - } - }, - "d3-collection": { - "version": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=" - }, - "d3-color": { - "version": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=" - }, - "d3-dispatch": { - "version": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" - }, - "d3-drag": { - "version": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.1.1.tgz", - "integrity": "sha1-tRVTBEM7GLo4cmshhNAJjoINxks=", - "requires": { - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-selection": "1.2.0" - } - }, - "d3-dsv": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", - "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", - "requires": { - "commander": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "rw": "1.3.3" - } - }, - "d3-ease": { - "version": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" - }, - "d3-force": { - "version": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", - "integrity": "sha1-zr88aU8QePzD1Nr45Wey+9cNTqM=", - "requires": { - "d3-collection": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-quadtree": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", - "d3-timer": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz" - } - }, - "d3-format": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.1.tgz", - "integrity": "sha512-U4zRVLDXW61bmqoo+OJ/V687e1T5nVd3TAKAJKgtpZ/P1JsMgyod0y9br+mlQOryTAACdiXI3wCjuERHFNp91w==" - }, - "d3-geo": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.0.tgz", - "integrity": "sha512-94YbAT+q5E/p+XEd4VKiNkQNicrrON3l6eoW70sC8aHjUGoBDJ/Ht8Z/hvhGJJTi2zhYBvUd34Mfqv4TG7we9A==", - "requires": { - "d3-array": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz" - } - }, - "d3-hierarchy": { - "version": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", - "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=" - }, - "d3-interpolate": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz", - "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==", - "requires": { - "d3-color": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz" - } - }, - "d3-path": { - "version": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", - "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=" - }, - "d3-polygon": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz", - "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI=" - }, - "d3-quadtree": { - "version": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", - "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=" - }, - "d3-queue": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz", - "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg=" - }, - "d3-random": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz", - "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM=" - }, - "d3-request": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz", - "integrity": "sha1-oQRKnvTsKMgkFxyTefrm15R0sZ8=", - "requires": { - "d3-collection": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-dsv": "1.0.8", - "xmlhttprequest": "1.8.0" - } - }, - "d3-scale": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", - "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", - "requires": { - "d3-array": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "d3-collection": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "d3-color": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "d3-format": "1.2.1", - "d3-interpolate": "1.1.6", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1" - } - }, - "d3-selection": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.2.0.tgz", - "integrity": "sha512-xW2Pfcdzh1gOaoI+LGpPsLR2VpBQxuFoxvrvguK8ZmrJbPIVvfNG6pU6GNfK41D6Qz15sj61sbW/AFYuukwaLQ==" - }, - "d3-shape": { - "version": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", - "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", - "requires": { - "d3-path": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz" - } - }, - "d3-time": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz", - "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==" - }, - "d3-time-format": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", - "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", - "requires": { - "d3-time": "1.0.8" - } - }, - "d3-timer": { - "version": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", - "integrity": "sha1-35ZQylh/bJZgf/TmDMOCKejdhTE=" - }, - "d3-transition": { - "version": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.0.tgz", - "integrity": "sha1-z8hcdOUjkyQpBUZiNXKZBWDDlm8=", - "requires": { - "d3-color": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-ease": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "d3-interpolate": "1.1.6", - "d3-selection": "1.2.0", - "d3-timer": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz" - } - }, - "d3-voronoi": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", - "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=" - }, - "d3-zoom": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz", - "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==", - "requires": { - "d3-dispatch": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "d3-drag": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.1.1.tgz", - "d3-interpolate": "1.1.6", - "d3-selection": "1.2.0", - "d3-transition": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.0.tgz" + "es5-ext": "0.10.38" } }, "dashdash": { - "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } } }, - "date-fns": { - "version": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.0-alpha.7.tgz", - "integrity": "sha1-JFrRb5V2Tqur+ywKQf1dAzwg5Xo=" - }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -2469,32 +1979,32 @@ "dev": true }, "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "ms": "2.0.0" } }, "decamelize": { - "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, - "default-require-extensions": { - "version": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", - "dev": true, - "requires": { - "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" - } - }, "define-properties": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", @@ -2505,6 +2015,15 @@ "object-keys": "1.0.11" } }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -2518,23 +2037,39 @@ "dev": true, "requires": { "globby": "6.1.0", - "is-path-cwd": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "is-path-in-cwd": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", "p-map": "1.2.0", "pify": "3.0.0", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + "rimraf": "2.6.2" }, "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } } } }, "delayed-stream": { - "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, @@ -2551,8 +2086,9 @@ "dev": true }, "depd": { - "version": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, "des.js": { @@ -2561,7 +2097,7 @@ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "minimalistic-assert": "1.0.0" } }, @@ -2572,11 +2108,12 @@ "dev": true }, "detect-indent": { - "version": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + "repeating": "2.0.1" } }, "detect-node": { @@ -2585,16 +2122,6 @@ "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", "dev": true }, - "di": { - "version": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha1-sdhVB9rzlkgo3lSzfQ1zumfdpWw=", - "dev": true - }, "diffie-hellman": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", @@ -2603,7 +2130,17 @@ "requires": { "bn.js": "4.11.8", "miller-rabin": "4.0.1", - "randombytes": "2.0.5" + "randombytes": "2.0.6" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "path-type": "3.0.0" } }, "dns-equal": { @@ -2613,13 +2150,13 @@ "dev": true }, "dns-packet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.2.2.tgz", - "integrity": "sha1-qKJr7HZGQ4lj/Ibgb4+LFtbIv3o=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", "dev": true, "requires": { "ip": "1.1.5", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "safe-buffer": "5.1.1" } }, "dns-txt": { @@ -2648,17 +2185,6 @@ } } }, - "dom-serialize": { - "version": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "ent": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "void-elements": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" - } - }, "dom-serializer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", @@ -2678,9 +2204,9 @@ } }, "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, "domelementtype": { @@ -2698,10 +2224,6 @@ "domelementtype": "1.3.0" } }, - "domino": { - "version": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz", - "integrity": "sha1-VKQVTsrpaGFmgPj+ujzt/zVccfQ=" - }, "domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", @@ -2713,28 +2235,30 @@ } }, "duplexify": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", - "integrity": "sha1-ThUWvmiDi8kKSZlPCzmm5ZYL780=", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz", + "integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==", "dev": true, "requires": { - "end-of-stream": "1.4.0", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.3", "stream-shift": "1.0.0" } }, "ecc-jsbn": { - "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "dev": true, "optional": true, "requires": { - "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + "jsbn": "0.1.1" } }, "ee-first": { - "version": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, @@ -2745,9 +2269,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.28", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.28.tgz", - "integrity": "sha1-jdTmRYCGZE6fnwoc8y4qH53/2e4=", + "version": "1.3.32", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.32.tgz", + "integrity": "sha1-EdBoTAhA4APEvoko+KxfNdvCtOY=", "dev": true }, "elliptic": { @@ -2760,7 +2284,7 @@ "brorand": "1.1.0", "hash.js": "1.1.3", "hmac-drbg": "1.0.1", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "minimalistic-assert": "1.0.0", "minimalistic-crypto-utils": "1.0.1" } @@ -2778,125 +2302,32 @@ "dev": true }, "encodeurl": { - "version": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, "end-of-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", - "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "once": "1.4.0" } }, - "engine.io": { - "version": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", "dev": true, "requires": { - "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "base64id": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-parser": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz" - }, - "dependencies": { - "accepts": { - "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true, - "requires": { - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" - } - }, - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" - } - }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-client": { - "version": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", - "dev": true, - "requires": { - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "component-inherit": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-parser": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "has-cors": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "parsejson": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "parseqs": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "xmlhttprequest-ssl": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "yeast": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz" - }, - "dependencies": { - "component-emitter": { - "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" - } - }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-parser": { - "version": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", - "dev": true, - "requires": { - "after": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "arraybuffer.slice": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "base64-arraybuffer": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "blob": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "wtf-8": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "graceful-fs": "4.1.11", "memory-fs": "0.4.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "tapable": "0.2.8" } }, - "ent": { - "version": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, "entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", @@ -2904,9 +2335,9 @@ "dev": true }, "errno": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.5.tgz", - "integrity": "sha512-tv2H+e3KBnMmNRuoVG24uorOj3XfYo+/nJJd07PUISRr0kaMKQKL5kyD+6ANXk1ZIIsvbORsjvHnCfC4KIc7uQ==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz", + "integrity": "sha512-IsORQDpaaSwcDP4ZZnHxgE85werpo34VYn1Ud3mq+eUsF593faR8oCZNXrROVkpFu2TsbrNhHin0aUrTsQ9vNw==", "dev": true, "requires": { "prr": "1.0.1" @@ -2946,9 +2377,9 @@ } }, "es5-ext": { - "version": "0.10.37", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", - "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=", + "version": "0.10.38", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.38.tgz", + "integrity": "sha512-jCMyePo7AXbUESwbl8Qi01VSH2piY9s/a3rSU/5w/MlTIx8HPL1xn2InGN8ejt/xulcJgnTO7vqNtOAxzYd2Kg==", "dev": true, "requires": { "es6-iterator": "2.0.3", @@ -2962,7 +2393,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-symbol": "3.1.1" } }, @@ -2973,7 +2404,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-iterator": "2.0.3", "es6-set": "0.1.5", "es6-symbol": "3.1.1", @@ -2987,7 +2418,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", "event-emitter": "0.3.5" @@ -3000,7 +2431,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37" + "es5-ext": "0.10.38" } }, "es6-weak-map": { @@ -3010,18 +2441,20 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-iterator": "2.0.3", "es6-symbol": "3.1.1" } }, "escape-html": { - "version": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", "dev": true }, "escape-string-regexp": { - "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, @@ -3038,7 +2471,8 @@ } }, "esprima": { - "version": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, @@ -3049,7 +2483,7 @@ "dev": true, "requires": { "estraverse": "4.2.0", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "object-assign": "4.1.1" } }, "estraverse": { @@ -3059,7 +2493,8 @@ "dev": true }, "esutils": { - "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, @@ -3076,11 +2511,12 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37" + "es5-ext": "0.10.38" } }, "eventemitter3": { - "version": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", "dev": true }, @@ -3102,11 +2538,11 @@ "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { "md5.js": "1.3.4", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "safe-buffer": "5.1.1" } }, "execa": { @@ -3132,69 +2568,27 @@ "requires": { "lru-cache": "4.1.1", "shebang-command": "1.2.0", - "which": "https://registry.npmjs.org/which/-/which-1.3.0.tgz" - } - } - } - }, - "exit": { - "version": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-braces": { - "version": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", - "dev": true, - "requires": { - "array-slice": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "array-unique": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "braces": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz" - }, - "dependencies": { - "braces": { - "version": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", - "dev": true, - "requires": { - "expand-range": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz" - } - }, - "expand-range": { - "version": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", - "dev": true, - "requires": { - "is-number": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz" + "which": "1.3.0" } - }, - "is-number": { - "version": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true - }, - "repeat-string": { - "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", - "dev": true } } }, "expand-brackets": { - "version": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" + "is-posix-bracket": "0.1.1" } }, "expand-range": { - "version": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz" + "fill-range": "2.2.3" } }, "exports-loader": { @@ -3204,7 +2598,7 @@ "dev": true, "requires": { "loader-utils": "1.1.0", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "source-map": "0.5.7" } }, "express": { @@ -3215,33 +2609,33 @@ "requires": { "accepts": "1.3.4", "array-flatten": "1.1.1", - "body-parser": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "content-type": "1.0.4", + "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "debug": "2.6.9", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", "etag": "1.8.1", "finalhandler": "1.1.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "1.1.2", - "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "on-finished": "2.3.0", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", "proxy-addr": "2.0.2", - "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", "send": "0.16.1", "serve-static": "1.13.1", "setprototypeof": "1.1.0", - "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "utils-merge": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", "vary": "1.1.2" }, "dependencies": { @@ -3250,27 +2644,37 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true } } }, "extend": { - "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", "dev": true }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, "extglob": { - "version": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" - }, - "dependencies": { - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - } + "is-extglob": "1.0.0" } }, "extract-text-webpack-plugin": { @@ -3279,24 +2683,33 @@ "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", "dev": true, "requires": { - "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "async": "2.6.0", "loader-utils": "1.1.0", "schema-utils": "0.3.0", "webpack-sources": "1.1.0" } }, "extsprintf": { - "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, "fast-deep-equal": { - "version": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", "dev": true }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, "fastparse": { - "version": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", "dev": true }, @@ -3310,9 +2723,9 @@ } }, "file-loader": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.5.tgz", - "integrity": "sha1-kcJba2++VtrpnxCkJf1kkztcnao=", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.6.tgz", + "integrity": "sha512-873ztuL+/hfvXbLDJ262PGO6XjERnybJu2gW1/5j8HUfxSiFJI9Hj/DhZ50ZGRUxBvuNiazb/cM2rh9pqrxP6Q==", "dev": true, "requires": { "loader-utils": "1.1.0", @@ -3320,39 +2733,22 @@ } }, "filename-regex": { - "version": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", "dev": true }, - "fileset": { - "version": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - } - }, "fill-range": { - "version": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "isobject": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "randomatic": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - }, - "dependencies": { - "isobject": { - "version": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - } - } + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "finalhandler": { @@ -3361,13 +2757,13 @@ "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" } }, "find-cache-dir": { @@ -3382,13 +2778,12 @@ } }, "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + "locate-path": "2.0.0" } }, "flatten": { @@ -3403,22 +2798,23 @@ "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "inherits": "2.0.3", + "readable-stream": "2.3.3" } }, "for-in": { - "version": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + "for-in": "1.0.2" } }, "foreach": { @@ -3428,18 +2824,20 @@ "dev": true }, "forever-agent": { - "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true }, "form-data": { - "version": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", "dev": true, "requires": { - "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" } }, "forwarded": { @@ -3448,6 +2846,15 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -3460,16 +2867,8 @@ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" - } - }, - "fs-access": { - "version": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz" + "inherits": "2.0.3", + "readable-stream": "2.3.3" } }, "fs-extra": { @@ -3478,7 +2877,7 @@ "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "graceful-fs": "4.1.11", "jsonfile": "4.0.0", "universalify": "0.1.1" } @@ -3489,202 +2888,1094 @@ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "graceful-fs": "4.1.11", "iferr": "0.1.5", "imurmurhash": "0.1.4", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "readable-stream": "2.3.3" } }, "fs.realpath": { - "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "wide-align": "1.1.2" - } - }, - "gaze": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", - "dev": true, - "optional": true, - "requires": { - "globule": "1.2.0" - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true, - "optional": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", "dev": true, "optional": true, "requires": { - "is-property": "1.0.2" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "getpass": { - "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - }, - "glob": { - "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "dev": true, - "requires": { - "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - } - }, - "glob-base": { - "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" }, "dependencies": { - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "abbrev": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true }, - "is-glob": { - "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "ajv": { + "version": "4.11.8", + "bundled": true, "dev": true, + "optional": true, "requires": { - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } - } - } - }, - "glob-parent": { - "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" - }, - "dependencies": { - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, "dev": true }, - "is-glob": { - "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "aproba": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, "dev": true, + "optional": true, "requires": { - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + "delegates": "1.0.0", + "readable-stream": "2.2.9" } - } - } - }, - "globals": { - "version": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", - "dev": true - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - } - }, - "globule": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", - "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", - "dev": true, - "optional": true, - "requires": { - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - } - }, - "graceful-fs": { - "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "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, + "requires": { + "wrappy": "1.0.2" + } + }, + "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.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "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.2.9", + "bundled": true, + "dev": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.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 + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "gaze": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "dev": true, + "optional": true, + "requires": { + "globule": "1.2.0" + } + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true, + "optional": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "optional": true, + "requires": { + "is-property": "1.0.2" + } + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, - "hammerjs": { - "version": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "glob": "7.1.2", + "ignore": "3.3.7", + "pify": "3.0.0", + "slash": "1.0.0" + } + }, + "globule": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.5", + "minimatch": "3.0.4" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true }, "handle-thing": { "version": "1.2.5", @@ -3693,17 +3984,34 @@ "dev": true }, "har-schema": { - "version": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true, + "optional": true }, "har-validator": { - "version": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "dev": true, + "optional": true, "requires": { - "ajv": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", - "har-schema": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" + "ajv": "4.11.8", + "har-schema": "1.0.5" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + } } }, "has": { @@ -3716,36 +4024,18 @@ } }, "has-ansi": { - "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - } - }, - "has-binary": { - "version": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", - "dev": true, - "requires": { - "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, - "dependencies": { - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } + "ansi-regex": "2.1.1" } }, - "has-cors": { - "version": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, "has-flag": { - "version": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, "has-unicode": { @@ -3754,34 +4044,95 @@ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, "hash-base": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "inherits": "2.0.3" } }, "hash.js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha1-NA3tvmKQGHFRweodd3o0SJNd+EY=", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "minimalistic-assert": "1.0.0" } }, "hawk": { - "version": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha1-r02RTrBl+bXOTZ0RwcshJu7MMDg=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { - "boom": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "cryptiles": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "sntp": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz" + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" } }, "he": { @@ -3802,22 +4153,15 @@ } }, "hoek": { - "version": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha1-ctnQdU9/4lyi0BrY+PmpRJqJUm0=", + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", "dev": true }, - "homedir-polyfill": { - "version": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "dev": true, - "requires": { - "parse-passwd": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" - } - }, "hosted-git-info": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha1-bWDjSzq7yDEwYsO3mO+NkBoHrzw=", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, "hpack.js": { @@ -3826,9 +4170,9 @@ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "inherits": "2.0.3", "obuf": "1.1.1", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "readable-stream": "2.3.3", "wbuf": "1.7.2" } }, @@ -3845,9 +4189,9 @@ "dev": true }, "html-minifier": { - "version": "3.5.7", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.7.tgz", - "integrity": "sha512-GISXn6oKDo7+gVpKOgZJTbHMCUI2TSGfpg/8jgencWhWJsvEmsvp3M8emX7QocsXsYznWloLib3OeSfeyb/ewg==", + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.8.tgz", + "integrity": "sha512-WX7D6PB9PFq05fZ1/CyxPUuyqXed6vh2fGOM80+zJT5wAO93D/cUjLs0CcbBFjQmlwmCgRvl97RurtArIpOnkw==", "dev": true, "requires": { "camel-case": "3.0.0", @@ -3857,15 +4201,7 @@ "ncname": "1.0.0", "param-case": "2.1.1", "relateurl": "0.2.7", - "uglify-js": "3.2.2" - }, - "dependencies": { - "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", - "dev": true - } + "uglify-js": "3.3.9" } }, "html-webpack-plugin": { @@ -3874,10 +4210,10 @@ "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", "dev": true, "requires": { - "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "html-minifier": "3.5.7", + "bluebird": "3.5.1", + "html-minifier": "3.5.8", "loader-utils": "0.2.17", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "lodash": "4.17.5", "pretty-error": "2.1.1", "toposort": "1.0.6" }, @@ -3891,7 +4227,7 @@ "big.js": "3.2.0", "emojis-list": "2.1.0", "json5": "0.5.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "object-assign": "4.1.1" } } } @@ -3929,8 +4265,8 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", "string_decoder": "0.10.31" } @@ -3950,36 +4286,45 @@ "dev": true }, "http-errors": { - "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", "dev": true, "requires": { - "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "setprototypeof": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" }, "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, "setprototypeof": { - "version": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", "dev": true } } }, "http-parser-js": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", - "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", "dev": true }, "http-proxy": { - "version": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", "dev": true, "requires": { - "eventemitter3": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "eventemitter3": "1.2.0", + "requires-port": "1.0.0" } }, "http-proxy-middleware": { @@ -3988,12 +4333,18 @@ "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", "dev": true, "requires": { - "http-proxy": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "http-proxy": "1.16.2", "is-glob": "3.1.0", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz" + "lodash": "4.17.5", + "micromatch": "2.3.11" }, "dependencies": { + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", @@ -4006,13 +4357,14 @@ } }, "http-signature": { - "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true, "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" } }, "https-browserify": { @@ -4021,19 +4373,11 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "https-proxy-agent": { - "version": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", - "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", - "dev": true, - "requires": { - "agent-base": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz" - } - }, "iconv-lite": { - "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=" + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true }, "icss-replace-symbols": { "version": "1.1.0", @@ -4047,61 +4391,7 @@ "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", "dev": true, "requires": { - "postcss": "6.0.14" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha1-tepI78nBeT3MybR2fJORTT8tUro=", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha1-VTTHIRRznnXQr88BfbhTCZ9WKIU=", - "dev": true, - "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } + "postcss": "6.0.17" } }, "ieee754": { @@ -4116,6 +4406,12 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, "image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -4124,9 +4420,9 @@ "optional": true }, "import-local": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-0.1.1.tgz", - "integrity": "sha1-sReVcqrNwRxqkQCftDDbyrX2aKg=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", "dev": true, "requires": { "pkg-dir": "2.0.0", @@ -4152,7 +4448,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + "repeating": "2.0.1" } }, "indexes-of": { @@ -4162,29 +4458,27 @@ "dev": true }, "indexof": { - "version": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, "inflight": { - "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { - "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "ini": { - "version": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", - "dev": true - }, "internal-ip": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", @@ -4200,17 +4494,13 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, - "intl": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz", - "integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94=" - }, "invariant": { - "version": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { - "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -4237,6 +4527,23 @@ "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", "dev": true }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4244,16 +4551,18 @@ "dev": true }, "is-binary-path": { - "version": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz" + "binary-extensions": "1.11.0" } }, "is-buffer": { - "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { @@ -4271,12 +4580,48 @@ "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", "dev": true }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", @@ -4284,35 +4629,39 @@ "dev": true }, "is-dotfile": { - "version": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", "dev": true }, "is-equal-shallow": { - "version": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + "is-primitive": "2.0.0" } }, "is-extendable": { - "version": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, "is-finite": { - "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -4321,22 +4670,22 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + "number-is-nan": "1.0.1" } }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "1.0.0" } }, "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz", + "integrity": "sha512-Q2khNw+oBlWuaYvEEHtKSw/pCxD2L5Rc1C+UQme9X6JdRDh7m5D7HkozA0qa3DUkQ6VzCnEm8mVIQPyIRkI5sQ==", "dev": true, "optional": true, "requires": { @@ -4347,32 +4696,56 @@ } }, "is-number": { - "version": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + "kind-of": "3.2.2" + } + }, + "is-odd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", + "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", + "dev": true, + "requires": { + "is-number": "3.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + } } }, "is-path-cwd": { - "version": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", "dev": true }, "is-path-in-cwd": { - "version": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz" + "is-path-inside": "1.0.1" } }, "is-path-inside": { - "version": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" + "path-is-inside": "1.0.2" } }, "is-plain-obj": { @@ -4384,19 +4757,29 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } } }, "is-posix-bracket": { - "version": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", "dev": true }, "is-primitive": { - "version": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, @@ -4438,342 +4821,155 @@ "dev": true }, "is-typedarray": { - "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, "is-utf8": { - "version": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true - }, - "isexe": { - "version": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.1.14.tgz", - "integrity": "sha1-JbxXAffGgMD//5E95G42GaOm5oA=", - "dev": true, - "requires": { - "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "fileset": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "istanbul-lib-hook": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz", - "istanbul-lib-instrument": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz", - "istanbul-lib-report": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "istanbul-lib-source-maps": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz", - "istanbul-reports": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "js-yaml": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - } - }, - "istanbul-instrumenter-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz", - "integrity": "sha1-5UkpAKsLuoNe+oAkywC+mz7qJwA=", - "dev": true, - "requires": { - "convert-source-map": "1.5.1", - "istanbul-lib-instrument": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz", - "loader-utils": "0.2.17", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - } - } - } - }, - "istanbul-lib-coverage": { - "version": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "integrity": "sha1-c7+5mIhSmUFck9OKPprfeEp3qdo=", - "dev": true - }, - "istanbul-lib-hook": { - "version": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz", - "integrity": "sha1-3WYH8DB2V4/n1vKmMM8UO0m6zdw=", - "dev": true, - "requires": { - "append-transform": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz" - } - }, - "istanbul-lib-instrument": { - "version": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz", - "integrity": "sha1-ZvbJQhzJ7EcE928tsIS6kHiitTI=", - "dev": true, - "requires": { - "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz" - } - }, - "istanbul-lib-report": { - "version": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha1-8OVfVmVf+jQiIIC3oM1HYOFAX8k=", - "dev": true, - "requires": { - "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "path-parse": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" - } - }, - "istanbul-lib-source-maps": { - "version": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz", - "integrity": "sha1-pv4ay6jOCO68Y45XLilNJnAIqgw=", - "dev": true, - "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - } - }, - "istanbul-reports": { - "version": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha1-D7Lj9qqZIr085F0F2KtNXo4HvU8=", - "dev": true, - "requires": { - "handlebars": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz" - }, - "dependencies": { - "async": { - "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "camelcase": { - "version": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "optional": true - }, - "cliui": { - "version": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "optional": true, - "requires": { - "center-align": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "right-align": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" - }, - "dependencies": { - "wordwrap": { - "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "optional": true - } - } - }, - "handlebars": { - "version": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", - "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", - "dev": true, - "requires": { - "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "optimist": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "uglify-js": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz" - } - }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "optimist": { - "version": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" - } - }, - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" - } - }, - "uglify-js": { - "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "optional": true, - "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "yargs": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" - }, - "dependencies": { - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - } - } - }, - "yargs": { - "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" - } - } - } + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, - "jasmine": { - "version": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { - "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "jasmine-core": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz" - }, - "dependencies": { - "jasmine-core": { - "version": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - } + "isarray": "1.0.0" } }, - "jasmine-core": { - "version": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.6.4.tgz", - "integrity": "sha1-3skmzQqfoof7bbXHVfpIfnTOysU=", + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "jasmine-spec-reporter": { - "version": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.1.1.tgz", - "integrity": "sha1-Wm1Yq11hvqcwn7wnkjlRF1axtYg=", + "istanbul-instrumenter-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz", + "integrity": "sha512-alLSEFX06ApU75sm5oWcaVNaiss/bgMRiWTct3g0P0ZZTKjR+6QiCcuVOKDI1kWJgwHEnIXsv/dWm783kPpmtw==", "dev": true, "requires": { - "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz" + "convert-source-map": "1.5.1", + "istanbul-lib-instrument": "1.9.1", + "loader-utils": "1.1.0", + "schema-utils": "0.3.0" } }, - "jasminewd2": { - "version": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "istanbul-lib-coverage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "integrity": "sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q==", "dev": true }, + "istanbul-lib-instrument": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz", + "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", + "dev": true, + "requires": { + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.1", + "semver": "5.5.0" + } + }, "js-base64": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.0.tgz", - "integrity": "sha512-Wehd+7Pf9tFvGb+ydPm9TjYjV8X1YHOVyG8QyELZxEMqOhemVwGRmoG8iQ/soqI3n8v4xn59zaLxiCJiaaRzKA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", + "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", "dev": true }, "js-tokens": { - "version": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "js-yaml": { - "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "dev": true, "requires": { - "argparse": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "esprima": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" + "argparse": "1.0.9", + "esprima": "2.7.3" } }, "jsbn": { - "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true, "optional": true }, "jsesc": { - "version": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true }, "json-loader": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", "dev": true }, "json-schema": { - "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, "json-schema-traverse": { - "version": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, "json-stable-stringify": { - "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, + "optional": true, "requires": { - "jsonify": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + "jsonify": "0.0.0" } }, "json-stringify-safe": { - "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, "json3": { - "version": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", "dev": true }, @@ -4782,349 +4978,105 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - } - }, - "jsonify": { - "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "json-schema": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "verror": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - } - }, - "karma": { - "version": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", - "integrity": "sha1-hcwI6eCiLXzpzKN8ShvoJPaisa4=", - "dev": true, - "requires": { - "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "body-parser": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "combine-lists": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "connect": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", - "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "di": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "dom-serialize": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "expand-braces": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "http-proxy": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "isbinaryfile": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "log4js": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "optimist": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "qjobs": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "socket.io": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "useragent": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz" - }, - "dependencies": { - "lodash": { - "version": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "optimist": { - "version": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" - } - } - } - }, - "karma-chrome-launcher": { - "version": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz", - "integrity": "sha1-IWh5xorATY1RQOmWGboEtZr9Rs8=", - "dev": true, - "requires": { - "fs-access": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "which": "https://registry.npmjs.org/which/-/which-1.3.0.tgz" - } - }, - "karma-cli": { - "version": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz", - "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", - "dev": true, - "requires": { - "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz" - } - }, - "karma-coverage-istanbul-reporter": { - "version": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.3.0.tgz", - "integrity": "sha1-0ULNnFVzHJ42Pvc3To7xoxvr+ts=", - "dev": true, - "requires": { - "istanbul-api": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.1.14.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - } - }, - "karma-jasmine": { - "version": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz", - "integrity": "sha1-IuTAa/mhguUpTR9wXjczgRuBCs8=", - "dev": true - }, - "karma-jasmine-html-reporter": { - "version": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", - "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", - "dev": true, - "requires": { - "karma-jasmine": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz" - } - }, - "karma-source-map-support": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.2.0.tgz", - "integrity": "sha1-G/gee7SwiWJ6s1LsQXnhF8QGpUA=", - "dev": true, - "requires": { - "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" - } - }, - "killable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", - "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", - "dev": true - }, - "kind-of": { - "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" - } - }, - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "less": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", - "integrity": "sha1-zBJg9RyQCp7A2R+2mYE54CUHtjs=", - "dev": true, - "requires": { - "errno": "0.1.5", - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "image-size": "0.5.5", - "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "promise": "7.3.1", - "request": "2.81.0", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "optional": true, - "requires": { - "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" - } - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "caseless": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "tunnel-agent": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "optional": true, - "requires": { - "hoek": "2.16.3" - } + }, + "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.11" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "optional": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true } } }, + "karma-source-map-support": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.2.0.tgz", + "integrity": "sha1-G/gee7SwiWJ6s1LsQXnhF8QGpUA=", + "dev": true, + "requires": { + "source-map-support": "0.4.18" + } + }, + "killable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", + "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "less": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", + "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "dev": true, + "requires": { + "errno": "0.1.6", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.81.0", + "source-map": "0.5.7" + } + }, "less-loader": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.0.5.tgz", @@ -5133,7 +5085,7 @@ "requires": { "clone": "2.1.1", "loader-utils": "1.1.0", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + "pify": "2.3.0" }, "dependencies": { "clone": { @@ -5141,13 +5093,19 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true } } }, "license-webpack-plugin": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.1.1.tgz", - "integrity": "sha1-drLO3Mx48Tn9eHfldvdWz8FBuMI=", + "integrity": "sha512-TjKOyiC0exqd4Idy/4M8/DETR22dXBZks387DuS5LbslxHiMRXGx/Q2F/j9IUtvEoH5uFvt72vRgk/G6f8j3Dg==", "dev": true, "requires": { "ejs": "2.5.7" @@ -5159,11 +5117,19 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "graceful-fs": "4.1.11", "parse-json": "2.2.0", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } } }, "loader-runner": { @@ -5191,19 +5157,12 @@ "requires": { "p-locate": "2.0.0", "path-exists": "3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } } }, "lodash": { - "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", "dev": true }, "lodash.assign": { @@ -5232,9 +5191,9 @@ "dev": true }, "lodash.mergewith": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", - "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", "dev": true, "optional": true }, @@ -5250,60 +5209,25 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, - "log4js": { - "version": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", - "dev": true, - "requires": { - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "semver": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" - }, - "dependencies": { - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - } - }, - "semver": { - "version": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - }, - "string_decoder": { - "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, "loglevel": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.0.tgz", - "integrity": "sha1-rgyqVhERSYxboTcj1vtjHSQAOTQ=", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", "dev": true }, "longest": { - "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true }, "loose-envify": { - "version": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + "js-tokens": "3.0.2" } }, "loud-rejection": { @@ -5325,7 +5249,7 @@ "lru-cache": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "dev": true, "requires": { "pseudomap": "1.0.2", @@ -5341,7 +5265,7 @@ "magic-string": { "version": "0.22.4", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", - "integrity": "sha1-MQObTkA2Y5VhjB1s+Bk8U5F0df8=", + "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", "dev": true, "requires": { "vlq": "0.2.3" @@ -5350,23 +5274,16 @@ "make-dir": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", - "integrity": "sha1-GbQ2n+SMEW9Twq+VrRAsDjnoXVE=", + "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", "dev": true, "requires": { "pify": "3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } } }, - "make-error": { - "version": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", - "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, "map-obj": { @@ -5375,9 +5292,14 @@ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, - "material-design-icons-iconfont": { - "version": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-3.0.3.tgz", - "integrity": "sha1-FUoQhAR9Ticjf6f1o34Qdc7qbfI=" + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } }, "math-expression-evaluator": { "version": "1.2.17", @@ -5385,17 +5307,6 @@ "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", "dev": true }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "dev": true, - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" - } - }, "md5.js": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", @@ -5403,7 +5314,7 @@ "dev": true, "requires": { "hash-base": "3.0.4", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "inherits": "2.0.3" }, "dependencies": { "hash-base": { @@ -5412,14 +5323,15 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } } } }, "media-typer": { - "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -5429,7 +5341,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "1.1.0" + "mimic-fn": "1.2.0" } }, "memory-fs": { @@ -5438,8 +5350,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "0.1.5", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "errno": "0.1.6", + "readable-stream": "2.3.3" } }, "meow": { @@ -5449,15 +5361,23 @@ "dev": true, "requires": { "camelcase-keys": "2.1.0", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "decamelize": "1.2.0", "loud-rejection": "1.6.0", "map-obj": "1.0.1", - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "minimist": "1.2.0", "normalize-package-data": "2.4.0", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "read-pkg-up": "1.0.1", "redent": "1.0.0", "trim-newlines": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "merge-descriptors": { @@ -5473,44 +5393,30 @@ "dev": true }, "micromatch": { - "version": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "array-unique": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "braces": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "expand-brackets": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "extglob": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "filename-regex": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "object.omit": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "parse-glob": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "regex-cache": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz" - }, - "dependencies": { - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" - } - } + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { "bn.js": "4.11.8", @@ -5518,27 +5424,30 @@ } }, "mime": { - "version": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, "mime-db": { - "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", "dev": true }, "mime-types": { - "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "dev": true, "requires": { - "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz" + "mime-db": "1.30.0" } }, "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimalistic-assert": { @@ -5554,36 +5463,59 @@ "dev": true }, "minimatch": { - "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + "brace-expansion": "1.1.8" } }, "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mississippi": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.0.tgz", - "integrity": "sha1-0gFYPrEjJ+PFwWQqQEqcrPlONPU=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.1.tgz", + "integrity": "sha512-/6rB8YXFbAtsUVRphIRQqB0+9c7VaPHCjVtvto+JqwVxgz8Zz+I+f68/JgQ+Pb4VlZb2svA9OtdXnHHsZz7ltg==", "dev": true, "requires": { "concat-stream": "1.6.0", - "duplexify": "3.5.1", - "end-of-stream": "1.4.0", + "duplexify": "3.5.3", + "end-of-stream": "1.4.1", "flush-write-stream": "1.0.2", "from2": "2.3.0", "parallel-transform": "1.1.0", "pump": "1.0.3", - "pumpify": "1.3.5", + "pumpify": "1.4.0", "stream-each": "1.2.2", "through2": "2.0.3" } }, + "mixin-deep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", + "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "mixin-object": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", @@ -5591,7 +5523,7 @@ "dev": true, "requires": { "for-in": "0.1.8", - "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + "is-extendable": "0.1.1" }, "dependencies": { "for-in": { @@ -5603,24 +5535,14 @@ } }, "mkdirp": { - "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - }, - "dependencies": { - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } + "minimist": "0.0.8" } }, - "moment": { - "version": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -5630,24 +5552,25 @@ "aproba": "1.2.0", "copy-concurrently": "1.0.5", "fs-write-stream-atomic": "1.0.10", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", "run-queue": "1.0.3" } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "multicast-dns": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.1.tgz", - "integrity": "sha512-uV3/ckdsffHx9IrGQrx613mturMdMqQ06WTq+C09NsStJ9iNG6RcUWgPKs1Rfjy+idZT6tfQoXEusGNnEZhT3w==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "requires": { - "dns-packet": "1.2.2", - "thunky": "0.1.0" + "dns-packet": "1.3.1", + "thunky": "1.0.2" } }, "multicast-dns-service-types": { @@ -5656,11 +5579,6 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, - "mydaterangepicker": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/mydaterangepicker/-/mydaterangepicker-4.1.12.tgz", - "integrity": "sha1-DR5AnpkvKxOlxPAvr46oEDhB5UY=" - }, "nan": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", @@ -5668,6 +5586,45 @@ "dev": true, "optional": true }, + "nanomatch": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.7.tgz", + "integrity": "sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "is-odd": "1.0.0", + "kind-of": "5.1.0", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "ncname": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", @@ -5678,39 +5635,24 @@ } }, "negotiator": { - "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "dev": true }, - "ng2-charts": { - "version": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-1.6.0.tgz", - "integrity": "sha1-EIohM/9iqGI4lSQPrb3b6ilR8p0=", - "requires": { - "chart.js": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.1.tgz" - } - }, - "ng2-cookies": { - "version": "https://registry.npmjs.org/ng2-cookies/-/ng2-cookies-1.0.12.tgz", - "integrity": "sha1-Pz5hPgE3sGSbcFxngHS0vQgUnMw=" - }, - "ngx-loading": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/ngx-loading/-/ngx-loading-1.0.9.tgz", - "integrity": "sha1-0M6Didq7a1LDAT8ePRYWuWjwrR4=" - }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "dev": true, "requires": { "lower-case": "1.1.4" } }, "node-forge": { - "version": "0.6.33", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.33.tgz", - "integrity": "sha1-RjgRh59XPUUVWtap9D3ClujoXrw=", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", + "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", "dev": true }, "node-gyp": { @@ -5721,18 +5663,18 @@ "optional": true, "requires": { "fstream": "1.0.11", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", "nopt": "3.0.6", "npmlog": "4.1.2", "osenv": "0.1.4", - "request": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "request": "2.81.0", + "rimraf": "2.6.2", "semver": "5.3.0", "tar": "2.2.1", - "which": "https://registry.npmjs.org/which/-/which-1.3.0.tgz" + "which": "1.3.0" }, "dependencies": { "nopt": { @@ -5766,19 +5708,19 @@ "console-browserify": "1.1.0", "constants-browserify": "1.0.0", "crypto-browserify": "3.12.0", - "domain-browser": "1.1.7", + "domain-browser": "1.2.0", "events": "1.1.1", "https-browserify": "1.0.0", "os-browserify": "0.3.0", "path-browserify": "0.0.0", "process": "0.11.10", - "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "punycode": "1.4.1", "querystring-es3": "0.2.1", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "readable-stream": "2.3.3", "stream-browserify": "2.0.1", - "stream-http": "2.7.2", - "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "timers-browserify": "2.0.4", + "stream-http": "2.8.0", + "string_decoder": "1.0.3", + "timers-browserify": "2.0.6", "tty-browserify": "0.0.0", "url": "0.11.0", "util": "0.10.3", @@ -5803,13 +5745,13 @@ "cross-spawn": "3.0.1", "gaze": "1.1.2", "get-stdin": "4.0.1", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "glob": "7.1.2", "in-publish": "2.0.0", "lodash.assign": "4.2.0", "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.0", + "lodash.mergewith": "4.6.1", "meow": "3.7.0", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "mkdirp": "0.5.1", "nan": "2.8.0", "node-gyp": "3.6.2", "npmlog": "4.1.2", @@ -5819,28 +5761,11 @@ "true-case-path": "1.0.2" }, "dependencies": { - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "caseless": { "version": "0.11.0", @@ -5855,77 +5780,24 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "supports-color": "2.0.0" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "optional": true, - "requires": { - "chalk": "1.1.3", - "commander": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "is-my-json-valid": "2.16.1", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", "dev": true, "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" + "chalk": "1.1.3", + "commander": "2.12.2", + "is-my-json-valid": "2.17.1", + "pinkie-promise": "2.0.1" } }, "qs": { @@ -5943,35 +5815,25 @@ "optional": true, "requires": { "aws-sign2": "0.6.0", - "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "aws4": "1.6.0", "caseless": "0.11.0", - "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", "form-data": "2.1.4", "har-validator": "2.0.6", "hawk": "3.1.3", "http-signature": "1.1.1", - "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", "qs": "6.3.2", - "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", "tunnel-agent": "0.4.3", - "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "optional": true, - "requires": { - "hoek": "2.16.3" + "uuid": "3.2.1" } }, "supports-color": { @@ -6002,21 +5864,22 @@ "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { "hosted-git-info": "2.5.0", "is-builtin-module": "1.0.0", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "semver": "5.5.0", "validate-npm-package-license": "3.0.1" } }, "normalize-path": { - "version": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" + "remove-trailing-separator": "1.1.0" } }, "normalize-range": { @@ -6031,7 +5894,7 @@ "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "dev": true, "requires": { - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "prepend-http": "1.0.4", "query-string": "4.3.4", "sort-keys": "1.1.2" @@ -6049,7 +5912,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { "are-we-there-yet": "1.1.4", @@ -6067,11 +5930,6 @@ "boolbase": "1.0.0" } }, - "null-check": { - "version": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", @@ -6079,24 +5937,81 @@ "dev": true }, "number-is-nan": { - "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "oauth-sign": { - "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true }, "object-assign": { - "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-component": { - "version": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + } + } }, "object-keys": { "version": "1.0.11", @@ -6104,22 +6019,47 @@ "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", "dev": true }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "object.omit": { - "version": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" }, "dependencies": { - "for-own": { - "version": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - } + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -6130,11 +6070,12 @@ "dev": true }, "on-finished": { - "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", "dev": true, "requires": { - "ee-first": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + "ee-first": "1.1.1" } }, "on-headers": { @@ -6144,27 +6085,23 @@ "dev": true }, "once": { - "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "wrappy": "1.0.2" } }, "opn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", - "integrity": "sha1-cs4jBqF9vqWP8QQYUzUrSo/HdRk=", + "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==", "dev": true, "requires": { "is-wsl": "1.1.0" } }, - "options": { - "version": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, "original": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", @@ -6181,7 +6118,7 @@ "dev": true, "requires": { "querystringify": "0.0.4", - "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "requires-port": "1.0.0" } } } @@ -6208,7 +6145,8 @@ } }, "os-tmpdir": { - "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -6219,7 +6157,7 @@ "dev": true, "requires": { "os-homedir": "1.0.2", - "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + "os-tmpdir": "1.0.2" } }, "p-finally": { @@ -6229,10 +6167,13 @@ "dev": true }, "p-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } }, "p-locate": { "version": "2.0.0", @@ -6240,13 +6181,19 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.1.0" + "p-limit": "1.2.0" } }, "p-map": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "pako": { @@ -6262,8 +6209,8 @@ "dev": true, "requires": { "cyclist": "0.2.2", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "inherits": "2.0.3", + "readable-stream": "2.3.3" } }, "param-case": { @@ -6289,29 +6236,15 @@ } }, "parse-glob": { - "version": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "is-dotfile": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" - }, - "dependencies": { - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" - } - } + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parse-json": { @@ -6323,62 +6256,45 @@ "error-ex": "1.3.1" } }, - "parse-passwd": { - "version": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parsejson": { - "version": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" - } - }, - "parseqs": { - "version": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" - } - }, - "parseuri": { - "version": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" - } - }, "parseurl": { - "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { - "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-is-inside": { - "version": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, @@ -6389,7 +6305,8 @@ "dev": true }, "path-parse": { - "version": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, @@ -6400,50 +6317,53 @@ "dev": true }, "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + "pify": "3.0.0" } }, "pbkdf2": { "version": "3.0.14", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha1-o14TxkeZsGzhUyD0WcIw5o5zut4=", + "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", "dev": true, "requires": { "create-hash": "1.1.3", "create-hmac": "1.1.6", "ripemd160": "2.0.1", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "sha.js": "2.4.9" + "safe-buffer": "5.1.1", + "sha.js": "2.4.10" } }, "performance-now": { - "version": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true, + "optional": true }, "pify": { - "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "pinkie": { - "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, "pinkie-promise": { - "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -6453,17 +6373,6 @@ "dev": true, "requires": { "find-up": "2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - } } }, "portfinder": { @@ -6473,8 +6382,8 @@ "dev": true, "requires": { "async": "1.5.2", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + "debug": "2.6.9", + "mkdirp": "0.5.1" }, "dependencies": { "async": { @@ -6485,38 +6394,59 @@ } } }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "version": "6.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", + "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", "dev": true, "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.0", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "5.1.0" }, "dependencies": { "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "supports-color": "2.0.0" + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" }, "dependencies": { "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 + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } } } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } } } }, @@ -6529,6 +6459,62 @@ "postcss": "5.2.18", "postcss-message-helpers": "2.0.0", "reduce-css-calc": "1.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-colormin": { @@ -6540,6 +6526,62 @@ "colormin": "1.1.2", "postcss": "5.2.18", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-convert-values": { @@ -6550,68 +6592,60 @@ "requires": { "postcss": "5.2.18", "postcss-value-parser": "3.3.0" - } - }, - "postcss-custom-properties": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-6.2.0.tgz", - "integrity": "sha1-XZKafwbpuE4PETNBlMC6mjCs++k=", - "dev": true, - "requires": { - "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "postcss": "6.0.14" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha1-tepI78nBeT3MybR2fJORTT8tUro=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha1-VTTHIRRznnXQr88BfbhTCZ9WKIU=", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } @@ -6623,6 +6657,62 @@ "dev": true, "requires": { "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-discard-duplicates": { @@ -6632,6 +6722,62 @@ "dev": true, "requires": { "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-discard-empty": { @@ -6641,6 +6787,62 @@ "dev": true, "requires": { "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-discard-overridden": { @@ -6650,6 +6852,62 @@ "dev": true, "requires": { "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-discard-unused": { @@ -6660,16 +6918,140 @@ "requires": { "postcss": "5.2.18", "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "uniqid": "4.1.1" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "postcss-import": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.0.0.tgz", + "integrity": "sha1-qWLi34LTvFptpqOGhBdHIE9B71s=", "dev": true, "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" + "postcss": "6.0.17", + "postcss-value-parser": "3.3.0", + "read-cache": "1.0.0", + "resolve": "1.5.0" } }, "postcss-load-config": { @@ -6679,7 +7061,7 @@ "dev": true, "requires": { "cosmiconfig": "2.2.2", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "postcss-load-options": "1.2.0", "postcss-load-plugins": "2.3.0" } @@ -6691,7 +7073,7 @@ "dev": true, "requires": { "cosmiconfig": "2.2.2", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "object-assign": "4.1.1" } }, "postcss-load-plugins": { @@ -6701,86 +7083,100 @@ "dev": true, "requires": { "cosmiconfig": "2.2.2", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "object-assign": "4.1.1" } }, "postcss-loader": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.9.tgz", - "integrity": "sha512-sgoXPtmgVT3aBAhU47Kig8oPF+mbXl8Unjvtz1Qj1q2D2EvSVJW2mKJNzxv5y/LvA9xWwuvdysvhc7Zn80UWWw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.0.tgz", + "integrity": "sha512-S/dKzpDwGFmP9g8eyCu9sUIV+/+3UooeTpYlsKf23qKDdrhHuA4pTSfytVu0rEJ0iDqUavXrgtOPq5KhNyNMOw==", "dev": true, "requires": { "loader-utils": "1.1.0", - "postcss": "6.0.14", + "postcss": "6.0.17", "postcss-load-config": "1.2.0", - "schema-utils": "0.3.0" + "schema-utils": "0.4.3" }, "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "schema-utils": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.3.tgz", + "integrity": "sha512-sgv/iF/T4/SewJkaVpldKC4WjSkz0JsOh2eKtxCPpCO1oR05+7MOF+H476HVRbLArkgA7j5TRJJ4p2jdFkUGQQ==", "dev": true, "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" + "ajv": "5.5.2", + "ajv-keywords": "2.1.1" } + } + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, "postcss-merge-longhand": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", @@ -6788,6 +7184,62 @@ "dev": true, "requires": { "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-merge-rules": { @@ -6801,6 +7253,72 @@ "postcss": "5.2.18", "postcss-selector-parser": "2.2.3", "vendors": "1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000803", + "electron-to-chromium": "1.3.32" + } + }, + "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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-message-helpers": { @@ -6815,9 +7333,65 @@ "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, "requires": { - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "postcss": "5.2.18", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-minify-gradients": { @@ -6828,6 +7402,62 @@ "requires": { "postcss": "5.2.18", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-minify-params": { @@ -6840,6 +7470,62 @@ "postcss": "5.2.18", "postcss-value-parser": "3.3.0", "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-minify-selectors": { @@ -6852,294 +7538,302 @@ "has": "1.0.1", "postcss": "5.2.18", "postcss-selector-parser": "2.2.3" - } - }, - "postcss-modules-extract-imports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", - "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", - "dev": true, - "requires": { - "postcss": "6.0.14" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha1-tepI78nBeT3MybR2fJORTT8tUro=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha1-VTTHIRRznnXQr88BfbhTCZ9WKIU=", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } }, + "postcss-modules-extract-imports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", + "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", + "dev": true, + "requires": { + "postcss": "6.0.17" + } + }, "postcss-modules-local-by-default": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", "dev": true, "requires": { - "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "postcss": "6.0.14" + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.17" + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.17" + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.17" + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha1-tepI78nBeT3MybR2fJORTT8tUro=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha1-VTTHIRRznnXQr88BfbhTCZ9WKIU=", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, "requires": { - "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "postcss": "6.0.14" + "is-absolute-url": "2.1.0", + "normalize-url": "1.9.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha1-tepI78nBeT3MybR2fJORTT8tUro=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha1-VTTHIRRznnXQr88BfbhTCZ9WKIU=", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "dev": true, "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.14" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha1-tepI78nBeT3MybR2fJORTT8tUro=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha1-VTTHIRRznnXQr88BfbhTCZ9WKIU=", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, "postcss-reduce-idents": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", @@ -7148,6 +7842,62 @@ "requires": { "postcss": "5.2.18", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-reduce-initial": { @@ -7157,6 +7907,62 @@ "dev": true, "requires": { "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-reduce-transforms": { @@ -7168,6 +7974,62 @@ "has": "1.0.1", "postcss": "5.2.18", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-selector-parser": { @@ -7191,6 +8053,62 @@ "postcss": "5.2.18", "postcss-value-parser": "3.3.0", "svgo": "0.7.2" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "postcss-unique-selectors": { @@ -7202,75 +8120,77 @@ "alphanum-sort": "1.0.2", "postcss": "5.2.18", "uniqs": "2.0.0" - } - }, - "postcss-url": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.0.tgz", - "integrity": "sha512-VBP6uf6iL3AZra23nkPkOEkS/5azj1xf/toRrjfkolfFEgg9Gyzg9UhJZeIsz12EGKZTNVeGbPa2XtaZm/iZvg==", - "dev": true, - "requires": { - "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "postcss": "6.0.14", - "xxhashjs": "0.2.1" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "supports-color": "4.5.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "4.5.0" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "1.0.0" } } } }, + "postcss-url": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.0.tgz", + "integrity": "sha512-VBP6uf6iL3AZra23nkPkOEkS/5azj1xf/toRrjfkolfFEgg9Gyzg9UhJZeIsz12EGKZTNVeGbPa2XtaZm/iZvg==", + "dev": true, + "requires": { + "mime": "1.6.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "postcss": "6.0.17", + "xxhashjs": "0.2.2" + } + }, "postcss-value-parser": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", @@ -7286,6 +8206,62 @@ "has": "1.0.1", "postcss": "5.2.18", "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } } }, "prepend-http": { @@ -7295,7 +8271,8 @@ "dev": true }, "preserve": { - "version": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, @@ -7316,14 +8293,15 @@ "dev": true }, "process-nextick-args": { - "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "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 }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "optional": true, "requires": { @@ -7336,118 +8314,6 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, - "protractor": { - "version": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz", - "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=", - "dev": true, - "requires": { - "@types/node": "https://registry.npmjs.org/@types/node/-/node-6.0.89.tgz", - "@types/q": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "@types/selenium-webdriver": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz", - "blocking-proxy": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.5.tgz", - "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "jasmine": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "jasminewd2": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "optimist": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "q": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "saucelabs": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.3.0.tgz", - "selenium-webdriver": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", - "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "webdriver-js-extender": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", - "webdriver-manager": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz" - }, - "dependencies": { - "chalk": { - "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - } - }, - "del": { - "version": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "is-path-cwd": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "is-path-in-cwd": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" - } - }, - "globby": { - "version": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "arrify": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - } - }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "optimist": { - "version": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" - } - }, - "q": { - "version": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "webdriver-manager": { - "version": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz", - "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", - "dev": true, - "requires": { - "adm-zip": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "del": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "ini": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "q": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "request": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "xml2js": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz" - }, - "dependencies": { - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - } - } - }, "proxy-addr": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", @@ -7480,7 +8346,7 @@ "browserify-rsa": "4.0.1", "create-hash": "1.1.3", "parse-asn1": "5.1.0", - "randombytes": "2.0.5" + "randombytes": "2.0.6" } }, "pump": { @@ -7489,23 +8355,36 @@ "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", "dev": true, "requires": { - "end-of-stream": "1.4.0", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "pumpify": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.3.5.tgz", - "integrity": "sha1-G2ccYZlAq8rqwK0OOjwWS+dgmTs=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", + "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", "dev": true, "requires": { - "duplexify": "3.5.1", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "pump": "1.0.3" + "duplexify": "3.5.3", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } } }, "punycode": { - "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, @@ -7515,15 +8394,12 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, - "qjobs": { - "version": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", - "dev": true - }, "qs": { - "version": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=", - "dev": true + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true, + "optional": true }, "query-string": { "version": "4.3.4", @@ -7531,7 +8407,7 @@ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "dev": true, "requires": { - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-assign": "4.1.1", "strict-uri-encode": "1.1.0" } }, @@ -7554,49 +8430,53 @@ "dev": true }, "randomatic": { - "version": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { - "version": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { - "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + "is-buffer": "1.1.6" } } } }, "kind-of": { - "version": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + "is-buffer": "1.1.6" } } } }, "randombytes": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", - "integrity": "sha1-3ACaJGuNCaF3tLegrne8Vw9LG3k=", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", "dev": true, "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "safe-buffer": "5.1.1" } }, "randomfill": { @@ -7605,24 +8485,26 @@ "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==", "dev": true, "requires": { - "randombytes": "2.0.5", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "randombytes": "2.0.6", + "safe-buffer": "5.1.1" } }, "range-parser": { - "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", "dev": true }, "raw-body": { - "version": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", "dev": true, "requires": { - "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" } }, "raw-loader": { @@ -7631,6 +8513,23 @@ "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", "dev": true }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -7640,6 +8539,25 @@ "load-json-file": "1.1.0", "normalize-package-data": "2.4.0", "path-type": "1.1.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } } }, "read-pkg-up": { @@ -7650,31 +8568,54 @@ "requires": { "find-up": "1.1.2", "read-pkg": "1.1.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + } } }, "readable-stream": { - "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "readdirp": { - "version": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "set-immediate-shim": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.3", + "set-immediate-shim": "1.0.1" } }, "redent": { @@ -7723,50 +8664,60 @@ } } }, - "reflect-metadata": { - "version": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", - "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=", - "dev": true - }, "regenerate": { - "version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha1-DDNtOYBVPXVcObWGrjsgqknIK38=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", + "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", "dev": true }, "regenerator-runtime": { - "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", - "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", "dev": true }, "regex-cache": { - "version": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" + "is-equal-shallow": "0.1.3" + } + }, + "regex-not": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", + "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" } }, "regexpu-core": { - "version": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { - "regenerate": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "regjsgen": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "regjsparser": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz" + "regenerate": "1.3.3", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "regjsgen": { - "version": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, "regjsparser": { - "version": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + "jsesc": "0.5.0" } }, "relateurl": { @@ -7776,7 +8727,8 @@ "dev": true }, "remove-trailing-separator": { - "version": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, @@ -7789,7 +8741,7 @@ "css-select": "1.2.0", "dom-converter": "0.1.4", "htmlparser2": "3.3.0", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "strip-ansi": "3.0.1", "utila": "0.3.3" }, "dependencies": { @@ -7802,50 +8754,55 @@ } }, "repeat-element": { - "version": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", "dev": true }, "repeat-string": { - "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "repeating": { - "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz" + "is-finite": "1.0.2" } }, "request": { - "version": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=", - "dev": true, - "requires": { - "aws-sign2": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "caseless": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "form-data": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "har-validator": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "hawk": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "http-signature": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "performance-now": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "tunnel-agent": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" } }, "require-directory": { @@ -7867,16 +8824,18 @@ "dev": true }, "requires-port": { - "version": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, "resolve": { - "version": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", - "integrity": "sha1-p1vgHFPaJdk0qY69DkxKcxL5KoY=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "dev": true, "requires": { - "path-parse": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz" + "path-parse": "1.0.5" } }, "resolve-cwd": { @@ -7894,20 +8853,28 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, "right-align": { - "version": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "requires": { - "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz" + "align-text": "0.1.4" } }, "rimraf": { - "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + "glob": "7.1.2" } }, "ripemd160": { @@ -7917,13 +8884,9 @@ "dev": true, "requires": { "hash-base": "2.0.2", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "inherits": "2.0.3" } }, - "roboto-fontface": { - "version": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.8.0.tgz", - "integrity": "sha1-AxqDyPeZMoAaV9g790PzclAWNJk=" - }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -7933,25 +8896,10 @@ "aproba": "1.2.0" } }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, - "rxjs": { - "version": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz", - "integrity": "sha1-KNQD8AcRIZZ/GK1mVWMlXVQjasM=", - "requires": { - "symbol-observable": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz" - } - }, - "rxjs-websockets": { - "version": "https://registry.npmjs.org/rxjs-websockets/-/rxjs-websockets-4.0.0.tgz", - "integrity": "sha1-qNBsdLhimp+dVkUO2lwxd1QsgPQ=" - }, "safe-buffer": { - "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "sass-graph": { @@ -7961,8 +8909,8 @@ "dev": true, "optional": true, "requires": { - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "glob": "7.1.2", + "lodash": "4.17.5", "scss-tokenizer": "0.2.3", "yargs": "7.1.0" } @@ -7970,35 +8918,20 @@ "sass-loader": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz", - "integrity": "sha1-6dXmwfFV+qMqSybXqbcQfCJeQPk=", + "integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==", "dev": true, "requires": { - "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "async": "2.6.0", "clone-deep": "0.3.0", "loader-utils": "1.1.0", "lodash.tail": "4.1.1", "pify": "3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "saucelabs": { - "version": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.3.0.tgz", - "integrity": "sha1-0kDoAJ33+ocwbsRXimm6O1xCT+4=", - "dev": true, - "requires": { - "https-proxy-agent": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz" } }, "sax": { - "version": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "schema-utils": { @@ -8007,7 +8940,7 @@ "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "dev": true, "requires": { - "ajv": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz" + "ajv": "5.5.2" } }, "scss-tokenizer": { @@ -8017,7 +8950,7 @@ "dev": true, "optional": true, "requires": { - "js-base64": "2.4.0", + "js-base64": "2.4.3", "source-map": "0.4.4" }, "dependencies": { @@ -8028,7 +8961,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" + "amdefine": "1.0.1" } } } @@ -8039,71 +8972,56 @@ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, - "selenium-webdriver": { - "version": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", - "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", - "dev": true, - "requires": { - "adm-zip": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "xml2js": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz" - }, - "dependencies": { - "tmp": { - "version": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - } - } - } - }, "selfsigned": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.1.tgz", - "integrity": "sha1-v4y3uDJWxFUeMTR8YxF3jbme7FI=", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.2.tgz", + "integrity": "sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=", "dev": true, "requires": { - "node-forge": "0.6.33" + "node-forge": "0.7.1" } }, "semver": { - "version": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4=", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz" - } - }, "send": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "debug": "2.6.9", + "depd": "1.1.2", "destroy": "1.0.4", - "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + } } }, + "serialize-javascript": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", + "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=", + "dev": true + }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -8112,22 +9030,22 @@ "requires": { "accepts": "1.3.4", "batch": "0.6.1", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz" + "debug": "2.6.9", + "escape-html": "1.0.3", + "http-errors": "1.6.2", + "mime-types": "2.1.17", + "parseurl": "1.3.2" } }, "serve-static": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", "dev": true, "requires": { - "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.1" } }, @@ -8137,11 +9055,33 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dev": true, + "requires": { + "to-object-path": "0.3.0" + } + }, "set-immediate-shim": { - "version": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "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", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -8151,17 +9091,17 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, "sha.js": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", - "integrity": "sha1-mPZIgEdLdPSji42p08Dy0QRjPn0=", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", + "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "shallow-clone": { @@ -8170,7 +9110,7 @@ "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", "dev": true, "requires": { - "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "is-extendable": "0.1.1", "kind-of": "2.0.1", "lazy-cache": "0.2.7", "mixin-object": "2.0.1" @@ -8182,7 +9122,7 @@ "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", "dev": true, "requires": { - "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + "is-buffer": "1.1.6" } } } @@ -8214,160 +9154,144 @@ "integrity": "sha1-IglwbxyFCp8dENDYQJGLRvJuG8k=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "debug": "2.6.9" } }, - "sntp": { - "version": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", - "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", - "dev": true, - "requires": { - "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" - } + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true }, - "socket.io": { - "version": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", + "snapdragon": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", + "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "socket.io-adapter": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "socket.io-client": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "2.0.2" }, "dependencies": { - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "is-descriptor": "0.1.6" } }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "object-assign": { - "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", - "dev": true, - "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz" - }, - "dependencies": { - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-client": { - "version": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "component-bind": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-client": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "object-component": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "to-array": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" - }, - "dependencies": { - "component-emitter": { - "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } }, - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } }, - "socket.io-parser": { - "version": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" - } - }, - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, "sockjs": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", - "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", "dev": true, "requires": { "faye-websocket": "0.10.0", - "uuid": "2.0.3" - }, - "dependencies": { - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true - } + "uuid": "3.2.1" } }, "sockjs-client": { @@ -8376,11 +9300,11 @@ "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "debug": "2.6.9", "eventsource": "0.1.6", "faye-websocket": "0.11.1", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "inherits": "2.0.3", + "json3": "3.3.2", "url-parse": "1.2.0" }, "dependencies": { @@ -8407,53 +9331,43 @@ "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha1-qqR0A/eyRakvvJfqCPJQ1gh+0IU=", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", "dev": true }, "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, - "source-map-loader": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.3.tgz", - "integrity": "sha1-1LDIzUfVTtzj5r+g9SP0UrWw5SE=", + "source-map-resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", "dev": true, "requires": { - "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "loader-utils": "0.2.17", - "source-map": "0.6.1" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - } + "atob": "2.0.3", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-support": { - "version": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "source-map": "0.5.7" } }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, "spdx-correct": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", @@ -8481,10 +9395,10 @@ "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "debug": "2.6.9", "handle-thing": "1.2.5", "http-deceiver": "1.2.7", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "safe-buffer": "5.1.1", "select-hose": "2.0.0", "spdy-transport": "2.0.20" } @@ -8495,46 +9409,165 @@ "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "debug": "2.6.9", "detect-node": "2.0.3", "hpack.js": "2.1.6", "obuf": "1.1.1", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1", "wbuf": "1.7.2" } }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "sprintf-js": { - "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sshpk": { - "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "ssri": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.1.0.tgz", + "integrity": "sha512-TevC8fgxQKTfQ1nWtM9GNzr3q5rrHNntG9CDMH1k3QhSZI6Kb+NbjLRs8oPFZa2Hgo7zoekL+UTvoEk7tsbjQg==", "dev": true, "requires": { - "asn1": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "bcrypt-pbkdf": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "dashdash": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "ecc-jsbn": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "getpass": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + "safe-buffer": "5.1.1" } }, - "ssri": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.0.0.tgz", - "integrity": "sha1-E8GTkLYGyCHyoQ0Cs1HBcpuU2M8=", + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, "statuses": { - "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", "dev": true }, @@ -8545,7 +9578,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "readable-stream": "2.3.3" } }, "stream-browserify": { @@ -8554,29 +9587,29 @@ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "inherits": "2.0.3", + "readable-stream": "2.3.3" } }, "stream-each": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha1-joxGP5HaiZF3h2WHP+TZYNj2Fr0=", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "dev": true, "requires": { - "end-of-stream": "1.4.0", + "end-of-stream": "1.4.1", "stream-shift": "1.0.0" } }, "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", + "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", "dev": true, "requires": { "builtin-status-codes": "3.0.0", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "inherits": "2.0.3", + "readable-stream": "2.3.3", "to-arraybuffer": "1.0.1", "xtend": "4.0.1" } @@ -8601,36 +9634,40 @@ "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + "strip-ansi": "3.0.1" } }, "string_decoder": { - "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "safe-buffer": "5.1.1" } }, "stringstream": { - "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", "dev": true }, "strip-ansi": { - "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + "ansi-regex": "2.1.1" } }, "strip-bom": { - "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + "is-utf8": "0.2.1" } }, "strip-eof": { @@ -8648,11 +9685,6 @@ "get-stdin": "4.0.1" } }, - "strip-json-comments": { - "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, "style-loader": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.13.2.tgz", @@ -8669,9 +9701,9 @@ "dev": true, "requires": { "css-parse": "1.7.0", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "debug": "2.6.9", "glob": "7.0.6", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "mkdirp": "0.5.1", "sax": "0.5.8", "source-map": "0.1.43" }, @@ -8682,12 +9714,12 @@ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", "dev": true, "requires": { - "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "sax": { @@ -8702,7 +9734,7 @@ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "dev": true, "requires": { - "amdefine": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" + "amdefine": "1.0.1" } } } @@ -8719,11 +9751,12 @@ } }, "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" + "has-flag": "2.0.0" } }, "svgo": { @@ -8733,17 +9766,19 @@ "dev": true, "requires": { "coa": "1.0.4", - "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "colors": "1.1.2", "csso": "2.3.2", - "js-yaml": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "sax": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", "whet.extend": "0.9.9" } }, "symbol-observable": { - "version": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", - "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true }, "tapable": { "version": "0.2.8", @@ -8760,29 +9795,23 @@ "requires": { "block-stream": "0.0.9", "fstream": "1.0.11", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "inherits": "2.0.3" } }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "through2": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "readable-stream": "2.3.3", "xtend": "4.0.1" } }, "thunky": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", - "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", + "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", "dev": true }, "time-stamp": { @@ -8792,27 +9821,14 @@ "dev": true }, "timers-browserify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", - "integrity": "sha1-lspT9LeUpefA4b18yIo3Ipj6AeY=", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", + "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", "dev": true, "requires": { "setimmediate": "1.0.5" } }, - "tmp": { - "version": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - } - }, - "to-array": { - "version": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -8820,10 +9836,120 @@ "dev": true }, "to-fast-properties": { - "version": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "to-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", + "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "regex-not": "1.0.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + } + } + }, "toposort": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.6.tgz", @@ -8831,17 +9957,18 @@ "dev": true }, "tough-cookie": { - "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "dev": true, "requires": { - "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + "punycode": "1.4.1" } }, "tree-kill": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", - "integrity": "sha1-WEZ4Yje0I5AU8F2xVrZDIS1MbzY=", + "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==", "dev": true }, "trim-newlines": { @@ -8851,7 +9978,8 @@ "dev": true }, "trim-right": { - "version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, @@ -8872,88 +10000,15 @@ "dev": true, "optional": true, "requires": { - "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } } } }, - "ts-node": { - "version": "https://registry.npmjs.org/ts-node/-/ts-node-3.2.2.tgz", - "integrity": "sha1-u9KOOK9Kqj6WB2xGbhsiAZfBo84=", - "dev": true, - "requires": { - "arrify": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "chalk": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "diff": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "make-error": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "tsconfig": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", - "v8flags": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", - "yn": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz" - } - }, - "tsconfig": { - "version": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", - "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", - "dev": true, - "requires": { - "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "strip-json-comments": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - }, - "dependencies": { - "strip-bom": { - "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "tsickle": { - "version": "https://registry.npmjs.org/tsickle/-/tsickle-0.24.1.tgz", - "integrity": "sha1-A5NDsgW/UXozOwcDl4iS+Ap9hI4=", - "dev": true, - "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" - } - }, - "tslib": { - "version": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", - "integrity": "sha1-3GBOutZLy/aW1hPabJVKoOfqHrY=" - }, - "tslint": { - "version": "https://registry.npmjs.org/tslint/-/tslint-5.7.0.tgz", - "integrity": "sha1-wl4NDJL6EgHCvDDoROCOaCtPNVI=", - "dev": true, - "requires": { - "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "commander": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "diff": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", - "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", - "tsutils": "https://registry.npmjs.org/tsutils/-/tsutils-2.12.1.tgz" - } - }, - "tsutils": { - "version": "https://registry.npmjs.org/tsutils/-/tsutils-2.12.1.tgz", - "integrity": "sha1-9Nlc4zkciXHkblTEzw7bCiHdWyQ=", - "dev": true, - "requires": { - "tslib": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz" - } - }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -8961,26 +10016,30 @@ "dev": true }, "tunnel-agent": { - "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, + "optional": true, "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "safe-buffer": "5.1.1" } }, "tweetnacl": { - "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true, "optional": true }, "type-is": { - "version": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", "dev": true, "requires": { - "media-typer": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + "media-typer": "0.3.0", + "mime-types": "2.1.17" } }, "typedarray": { @@ -8989,62 +10048,69 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "typescript": { - "version": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz", - "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", - "dev": true - }, "uglify-js": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.2.2.tgz", - "integrity": "sha512-++1NO/zZIEdWf6cDIGceSJQPX31SqIpbVAHwFG5+240MtZqPG/NIPoinj8zlXQtAfMBqEt1Jyv2FiLP3n9gVhQ==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.9.tgz", + "integrity": "sha512-J2t8B5tj9JdPTW4+sNZXmiIWHzTvcoITkaqzTiilu/biZF/9crqf/Fi7k5hqbOmVRh9/hVNxAxBYIMF7N6SqMQ==", "dev": true, "requires": { - "commander": "2.12.2", + "commander": "2.13.0", "source-map": "0.6.1" }, "dependencies": { "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "uglify-to-browserify": { - "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, "uglifyjs-webpack-plugin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.2.tgz", - "integrity": "sha512-k07cmJTj+8vZMSc3BaQ9uW7qVl2MqDts4ti4KaNACXEcXSw2vQM2S8olSk/CODxvcSFGvUHzNSqA8JQlhgUJPw==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.8.tgz", + "integrity": "sha512-XG8/QmR1pyPeE1kj2aigo5kos8umefB31zW+PMvAAytHSB0T/vQvN6sqt8+Sh+y0b0A7zlmxNi2dzRnj0wcqGA==", "dev": true, "requires": { - "cacache": "10.0.1", + "cacache": "10.0.2", "find-cache-dir": "1.0.0", - "schema-utils": "0.3.0", + "schema-utils": "0.4.3", + "serialize-javascript": "1.4.0", "source-map": "0.6.1", - "uglify-es": "3.2.2", + "uglify-es": "3.3.9", "webpack-sources": "1.1.0", "worker-farm": "1.5.2" }, "dependencies": { "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, + "schema-utils": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.3.tgz", + "integrity": "sha512-sgv/iF/T4/SewJkaVpldKC4WjSkz0JsOh2eKtxCPpCO1oR05+7MOF+H476HVRbLArkgA7j5TRJJ4p2jdFkUGQQ==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "ajv-keywords": "2.1.1" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9052,21 +10118,42 @@ "dev": true }, "uglify-es": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.2.2.tgz", - "integrity": "sha512-l+s5VLzFwGJfS+fbqaGf/Dfwo1MF13jLOF2ekL0PytzqEqQ6cVppvHf4jquqFok+35USMpKjqkYxy6pQyUcuug==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", "dev": true, "requires": { - "commander": "2.12.2", + "commander": "2.13.0", "source-map": "0.6.1" } } } }, - "ultron": { - "version": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } }, "uniq": { "version": "1.0.1", @@ -9114,16 +10201,69 @@ "dev": true }, "unpipe": { - "version": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", "dev": true }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -9145,22 +10285,22 @@ "url-loader": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", - "integrity": "sha1-oAenEJYg6dmI0UvOZ3od7Lmpk/c=", + "integrity": "sha512-h3qf9TNn53BpuXTTcpC+UehiRrl0Cv45Yr/xWayApjw6G8Bg2dGke7rIwDQ39piciWCWrC+WiqLjOh3SUp9n0Q==", "dev": true, "requires": { "loader-utils": "1.1.0", - "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "mime": "1.6.0", "schema-utils": "0.3.0" } }, "url-parse": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", - "integrity": "sha1-OhnoqqbQI93SfcxEy0/I9/7COYY=", + "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", "dev": true, "requires": { "querystringify": "1.0.0", - "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "requires-port": "1.0.0" }, "dependencies": { "querystringify": { @@ -9171,19 +10311,97 @@ } } }, - "useragent": { - "version": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", - "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "use": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", + "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", "dev": true, "requires": { - "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz" + "define-property": "0.2.5", + "isobject": "3.0.1", + "lazy-cache": "2.0.2" }, "dependencies": { - "lru-cache": { - "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true + }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "dev": true, + "requires": { + "set-getter": "0.1.0" + } } } }, @@ -9205,7 +10423,8 @@ } }, "util-deprecate": { - "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, @@ -9216,23 +10435,17 @@ "dev": true }, "utils-merge": { - "version": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, "uuid": { - "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true }, - "v8flags": { - "version": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", - "integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=", - "dev": true, - "requires": { - "homedir-polyfill": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz" - } - }, "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", @@ -9256,19 +10469,28 @@ "dev": true }, "verror": { - "version": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } } }, "vlq": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha1-jz5DKM9jsVQMDWfhsneDhviXWyY=", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", "dev": true }, "vm-browserify": { @@ -9277,23 +10499,18 @@ "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { - "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz" + "indexof": "0.0.1" } }, - "void-elements": { - "version": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, "watchpack": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", "dev": true, "requires": { - "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + "async": "2.6.0", + "chokidar": "1.7.0", + "graceful-fs": "4.1.11" } }, "wbuf": { @@ -9305,68 +10522,17 @@ "minimalistic-assert": "1.0.0" } }, - "web-animations-js": { - "version": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz", - "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA=" - }, - "webdriver-js-extender": { - "version": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", - "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", - "dev": true, - "requires": { - "@types/selenium-webdriver": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz", - "selenium-webdriver": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz" - }, - "dependencies": { - "adm-zip": { - "version": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", - "integrity": "sha1-ph7VrmkFw66lizplfSUDMJEFJzY=", - "dev": true - }, - "sax": { - "version": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", - "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=", - "dev": true - }, - "selenium-webdriver": { - "version": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", - "integrity": "sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU=", - "dev": true, - "requires": { - "adm-zip": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", - "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", - "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "xml2js": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz" - } - }, - "tmp": { - "version": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", - "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=", - "dev": true - }, - "xml2js": { - "version": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", - "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", - "dev": true, - "requires": { - "sax": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", - "xmlbuilder": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz" - } - } - } - }, "webpack": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz", "integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==", "dev": true, "requires": { - "acorn": "5.2.1", + "acorn": "5.4.1", "acorn-dynamic-import": "2.0.2", - "ajv": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", + "ajv": "5.5.2", "ajv-keywords": "2.1.1", - "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "async": "2.6.0", "enhanced-resolve": "3.4.1", "escope": "3.6.0", "interpret": "1.1.0", @@ -9375,9 +10541,9 @@ "loader-runner": "2.3.0", "loader-utils": "1.1.0", "memory-fs": "0.4.1", - "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "mkdirp": "0.5.1", "node-libs-browser": "2.1.0", - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "source-map": "0.5.7", "supports-color": "4.5.0", "tapable": "0.2.8", "uglifyjs-webpack-plugin": "0.4.6", @@ -9404,42 +10570,27 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "center-align": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "right-align": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" } }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "graceful-fs": "4.1.11", "parse-json": "2.2.0", - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pify": "2.3.0", "strip-bom": "3.0.0" } }, "os-locale": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { "execa": "0.7.0", @@ -9453,9 +10604,15 @@ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { - "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + "pify": "2.3.0" } }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -9480,7 +10637,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", @@ -9510,23 +10667,14 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", "yargs": "3.10.0" }, "dependencies": { @@ -9538,8 +10686,8 @@ "requires": { "camelcase": "1.2.1", "cliui": "2.1.0", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + "decamelize": "1.2.0", + "window-size": "0.1.0" } } } @@ -9550,7 +10698,7 @@ "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", "dev": true, "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "source-map": "0.5.7", "uglify-js": "2.8.29", "webpack-sources": "1.1.0" } @@ -9561,12 +10709,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -9575,7 +10717,7 @@ "requires": { "camelcase": "4.1.0", "cliui": "3.2.0", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "decamelize": "1.2.0", "get-caller-file": "1.0.2", "os-locale": "2.1.0", "read-pkg-up": "2.0.0", @@ -9601,7 +10743,7 @@ "dev": true, "requires": { "string-width": "1.0.2", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "strip-ansi": "3.0.1", "wrap-ansi": "2.1.0" }, "dependencies": { @@ -9613,7 +10755,7 @@ "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + "strip-ansi": "3.0.1" } } } @@ -9639,64 +10781,6 @@ } } }, - "webpack-concat-plugin": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/webpack-concat-plugin/-/webpack-concat-plugin-1.4.2.tgz", - "integrity": "sha512-HdV2xOq4twtL2ThR9NSCCQ888v1JBMpJfm3k2mA1I5LkS2+/6rv8q/bb9yTSaR0fVaMtANZi4Wkz0xc33MAt6w==", - "dev": true, - "requires": { - "md5": "2.2.1", - "uglify-js": "2.8.29" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "right-align": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "wordwrap": "0.0.2" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "yargs": "3.10.0" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" - } - } - } - }, "webpack-core": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", @@ -9719,7 +10803,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" + "amdefine": "1.0.1" } } } @@ -9732,29 +10816,21 @@ "requires": { "memory-fs": "0.4.1", "mime": "1.6.0", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", "time-stamp": "2.0.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - } } }, "webpack-dev-server": { - "version": "2.9.7", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.9.7.tgz", - "integrity": "sha512-Pu7uoQFgQj5RE5wmlfkpYSzihMKxulwEuO2xCsaMnAnyRSApwoVi3B8WCm9XbigyWTHaIMzYGkB90Vr6leAeTQ==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz", + "integrity": "sha512-ombhu5KsO/85sVshIDTyQ5HF3xjZR3N0sf5Ao6h3vFwpNyzInEzA1GV3QPVjTMLTNckp8PjfG1PFGznzBwS5lg==", "dev": true, "requires": { "ansi-html": "0.0.7", "array-includes": "3.0.3", "bonjour": "3.5.0", - "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "chokidar": "2.0.0", "compression": "1.7.1", "connect-history-api-fallback": "1.5.0", "debug": "3.1.0", @@ -9762,49 +10838,314 @@ "express": "4.16.2", "html-entities": "1.2.1", "http-proxy-middleware": "0.17.4", - "import-local": "0.1.1", + "import-local": "1.0.0", "internal-ip": "1.2.0", "ip": "1.1.5", "killable": "1.0.0", - "loglevel": "1.6.0", + "loglevel": "1.6.1", "opn": "5.1.0", "portfinder": "1.0.13", - "selfsigned": "1.10.1", + "selfsigned": "1.10.2", "serve-index": "1.9.1", - "sockjs": "0.3.18", + "sockjs": "0.3.19", "sockjs-client": "1.1.4", "spdy": "3.4.7", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "supports-color": "4.5.0", + "strip-ansi": "3.0.1", + "supports-color": "5.1.0", "webpack-dev-middleware": "1.12.2", "yargs": "6.6.0" }, "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "3.1.5", + "normalize-path": "2.1.1" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "braces": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", + "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.1" + } + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, + "chokidar": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.0.tgz", + "integrity": "sha512-OgXCNv2U6TnG04D3tth0gsvdbV4zdbxFG3sYUqcoQMoEFVd1j1pZR6TZ8iknC45o9IJ6PeQI/J6wT/+cHcniAw==", + "dev": true, + "requires": { + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.0", + "fsevents": "1.1.3", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "ms": "2.0.0" } }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "micromatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.5.tgz", + "integrity": "sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.0", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.7", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", "dev": true, "requires": { "has-flag": "2.0.0" @@ -9818,7 +11159,7 @@ "requires": { "camelcase": "3.0.0", "cliui": "3.2.0", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "decamelize": "1.2.0", "get-caller-file": "1.0.2", "os-locale": "1.4.0", "read-pkg-up": "1.0.1", @@ -9845,10 +11186,10 @@ "webpack-merge": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.1.tgz", - "integrity": "sha1-8Rl6Cpc+acb77rbWWCGaqMDBNVU=", + "integrity": "sha512-geQsZ86YkXOVOjvPC5yv3JSNnL6/X3Kzh935AQ/gJNEYXEfJDQFu/sdFuktS9OW2JcH/SJec8TGfRdrpHshH7A==", "dev": true, "requires": { - "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "lodash": "4.17.5" } }, "webpack-sources": { @@ -9884,7 +11225,7 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": "0.4.9", + "http-parser-js": "0.4.10", "websocket-extensions": "0.1.3" } }, @@ -9907,11 +11248,12 @@ "dev": true }, "which": { - "version": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + "isexe": "2.0.0" } }, "which-module": { @@ -9923,20 +11265,22 @@ "wide-align": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "requires": { "string-width": "1.0.2" } }, "window-size": { - "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true }, "wordwrap": { - "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true }, "worker-farm": { @@ -9945,7 +11289,7 @@ "integrity": "sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==", "dev": true, "requires": { - "errno": "0.1.5", + "errno": "0.1.6", "xtend": "4.0.1" } }, @@ -9956,62 +11300,21 @@ "dev": true, "requires": { "string-width": "1.0.2", - "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + "strip-ansi": "3.0.1" } }, "wrappy": { - "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "ws": { - "version": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "ultron": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" - } - }, - "wtf-8": { - "version": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, - "xhr2": { - "version": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", - "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" - }, "xml-char-classes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=", "dev": true }, - "xml2js": { - "version": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=", - "dev": true, - "requires": { - "sax": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "xmlbuilder": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz" - } - }, - "xmlbuilder": { - "version": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz", - "integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=", - "dev": true - }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, - "xmlhttprequest-ssl": { - "version": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", - "dev": true - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -10019,9 +11322,9 @@ "dev": true }, "xxhashjs": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.1.tgz", - "integrity": "sha1-m76b6JYUKXbfo0wGGy0GjEPTDeA=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", "dev": true, "requires": { "cuint": "0.2.2" @@ -10048,7 +11351,7 @@ "requires": { "camelcase": "3.0.0", "cliui": "3.2.0", - "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "decamelize": "1.2.0", "get-caller-file": "1.0.2", "os-locale": "1.4.0", "read-pkg-up": "1.0.1", @@ -10088,20 +11391,6 @@ "optional": true } } - }, - "yeast": { - "version": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yn": { - "version": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", - "dev": true - }, - "zone.js": { - "version": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.18.tgz", - "integrity": "sha1-jOyzl3/NGzCQVi/0Vw4oR+dStI0=" } } } diff --git a/ui/package.json b/ui/package.json index bd024984051..14eafd9329d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,70 +14,69 @@ }, "private": true, "dependencies": { - "@angular/animations": "^5.0.0", - "@angular/cdk": "^5.0.0", - "@angular/common": "^5.0.0", - "@angular/compiler": "^5.0.0", - "@angular/core": "^5.0.0", - "@angular/flex-layout": "2.0.0-beta.10-4905443", - "@angular/forms": "^5.0.0", - "@angular/http": "^5.0.0", - "@angular/material": "^5.0.0", - "@angular/platform-browser": "^5.0.0", - "@angular/platform-browser-dynamic": "^5.0.0", - "@angular/platform-server": "^5.0.0", - "@angular/router": "^5.0.0", - "@ngx-translate/core": "^9.0.1", + "@angular/animations": "^5.2.0", + "@angular/cdk": "^5.2.0", + "@angular/common": "^5.2.0", + "@angular/compiler": "^5.2.0", + "@angular/core": "^5.2.0", + "@angular/flex-layout": "2.0.0-beta.12", + "@angular/forms": "^5.2.0", + "@angular/http": "^5.2.0", + "@angular/material": "^5.2.0", + "@angular/platform-browser": "^5.2.0", + "@angular/platform-browser-dynamic": "^5.2.0", + "@angular/platform-server": "^5.2.0", + "@angular/router": "^5.2.0", + "@ngx-translate/core": "^9.1.1", "@types/d3": "4.12.0", - "angular2-toaster": "4.0.1", + "angular2-toaster": "5.0.0-alpha.1", "angular2-uuid": "^1.1.1", "chart.js": "2.7.1", "classlist.js": "^1.1.20150312", "core-js": "^2.4.1", - "d3": "4.12.0", + "d3": "4.13.0", "d3-array": "1.2.1", "d3-brush": "1.0.4", "d3-color": "1.0.3", "d3-force": "1.1.0", - "d3-format": "1.2.1", + "d3-format": "1.2.2", "d3-hierarchy": "1.1.5", "d3-interpolate": "1.1.6", - "d3-scale": "1.0.7", - "d3-selection": "1.2.0", + "d3-scale": "2.0.0", + "d3-selection": "1.3.0", "d3-shape": "1.2.0", "date-fns": "^2.0.0-alpha.7", "hammerjs": "2.0.8", "intl": "^1.2.5", "material-design-icons-iconfont": "3.0.3", - "mydaterangepicker": "^4.1.12", + "mydaterangepicker": "^4.2.1", "ng2-charts": "^1.6.0", "ng2-cookies": "^1.0.12", - "ngx-loading": "^1.0.9", + "ngx-loading": "^1.0.14", "roboto-fontface": "0.8.0", - "rxjs": "^5.5.2", + "rxjs": "^5.5.6", "rxjs-websockets": "4.0.0", "web-animations-js": "^2.3.1", - "zone.js": "^0.8.14" + "zone.js": "^0.8.19" }, "devDependencies": { - "@angular/cli": "1.6.0", - "@angular/compiler-cli": "^5.0.0", - "@angular/language-service": "^5.0.0", - "@types/jasmine": "~2.5.53", + "@angular/cli": "1.6.7", + "@angular/compiler-cli": "^5.2.0", + "@angular/language-service": "^5.2.0", + "@types/jasmine": "~2.8.3", "@types/jasminewd2": "~2.0.2", "@types/node": "~6.0.60", "codelyzer": "^4.0.1", - "jasmine-core": "~2.6.2", - "jasmine-spec-reporter": "~4.1.0", - "karma": "~1.7.0", - "karma-chrome-launcher": "~2.1.1", - "karma-cli": "~1.0.1", + "jasmine-core": "~2.8.0", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~2.0.0", + "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", "protractor": "~5.1.2", - "ts-node": "~3.2.0", - "tslint": "~5.7.0", - "typescript": "~2.4.2" + "ts-node": "~4.1.0", + "tslint": "~5.9.1", + "typescript": "~2.5.3" } -} +} \ No newline at end of file diff --git a/ui/src/tsconfig.spec.json b/ui/src/tsconfig.spec.json index 63d89ff283f..ac22a298aca 100644 --- a/ui/src/tsconfig.spec.json +++ b/ui/src/tsconfig.spec.json @@ -4,7 +4,6 @@ "outDir": "../out-tsc/spec", "baseUrl": "./", "module": "commonjs", - "target": "es5", "types": [ "jasmine", "node" diff --git a/ui/tslint.json b/ui/tslint.json index a2e30eff294..9963d6c3954 100644 --- a/ui/tslint.json +++ b/ui/tslint.json @@ -107,7 +107,6 @@ "variable-declaration": "nospace" } ], - "typeof-compare": true, "unified-signatures": true, "variable-name": false, "whitespace": [ diff --git a/ui/yarn.lock b/ui/yarn.lock index c274a0d5a54..52140ad2f7e 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2,54 +2,56 @@ # yarn lockfile v1 -"@angular-devkit/build-optimizer@~0.0.34": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.34.tgz#47a0482f5687db79d102cf9d416cf6fa6774d702" +"@angular-devkit/build-optimizer@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.42.tgz#402b0dda4883db91e2381c3ddc55888408a7894e" dependencies: loader-utils "^1.1.0" source-map "^0.5.6" - typescript "~2.6.1" + typescript "~2.6.2" webpack-sources "^1.0.1" -"@angular-devkit/core@0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.22.tgz#e90f46bf7ff47d260a767959267bc65ffee39ef1" +"@angular-devkit/core@0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.29.tgz#6fb319b45a62eff172318cbe256fdb24ef20af2b" dependencies: + ajv "~5.5.1" + chokidar "^1.7.0" + rxjs "^5.5.6" source-map "^0.5.6" -"@angular-devkit/schematics@~0.0.38": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.39.tgz#8821482d21f3d16798474983599c592063c881e7" +"@angular-devkit/schematics@0.0.52": + version "0.0.52" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.52.tgz#cbd2f42778b50d6422a254ffaec05ad4ef3cb6c0" dependencies: - "@angular-devkit/core" "0.0.22" "@ngtools/json-schema" "^1.1.0" - minimist "^1.2.0" - rxjs "^5.5.2" + rxjs "^5.5.6" -"@angular/animations@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.0.1.tgz#a92b2b186a6e5a31a9f1584911dd6aa7e16c5de1" +"@angular/animations@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.2.3.tgz#e0eb8d55f807e799df128af9150c075c9ab82adf" dependencies: tslib "^1.7.1" -"@angular/cdk@5.0.0-rc.2": - version "5.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-5.0.0-rc.2.tgz#65a76381a0481e92990af4a105cd3bcbc53ee2af" +"@angular/cdk@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-5.2.0.tgz#4368f6749e915cdcc75d325ae33fdb3f85a88108" dependencies: tslib "^1.7.1" -"@angular/cli@1.5.5": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.5.5.tgz#400ef304d88b13a82a72a4c4c42e2b53a0db04fd" +"@angular/cli@1.6.7": + version "1.6.7" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.6.7.tgz#e2323753c144b5de6c699bbebee688105a394641" dependencies: - "@angular-devkit/build-optimizer" "~0.0.34" - "@angular-devkit/schematics" "~0.0.38" + "@angular-devkit/build-optimizer" "0.0.42" + "@angular-devkit/core" "0.0.29" + "@angular-devkit/schematics" "0.0.52" "@ngtools/json-schema" "1.1.0" - "@ngtools/webpack" "1.8.5" - "@schematics/angular" "~0.1.8" - autoprefixer "^6.5.3" + "@ngtools/webpack" "1.9.7" + "@schematics/angular" "0.1.17" + autoprefixer "^7.2.3" chalk "~2.2.0" - circular-dependency-plugin "^3.0.0" + circular-dependency-plugin "^4.2.1" common-tags "^1.3.1" copy-webpack-plugin "^4.1.1" core-object "^3.1.0" @@ -58,16 +60,17 @@ denodeify "^1.2.1" ember-cli-string-utils "^1.0.0" exports-loader "^0.6.3" - extract-text-webpack-plugin "3.0.0" + extract-text-webpack-plugin "^3.0.2" file-loader "^1.1.5" fs-extra "^4.0.0" glob "^7.0.3" html-webpack-plugin "^2.29.0" - istanbul-instrumenter-loader "^2.0.0" + istanbul-instrumenter-loader "^3.0.0" karma-source-map-support "^1.2.0" less "^2.7.2" less-loader "^4.0.5" license-webpack-plugin "^1.0.0" + loader-utils "1.1.0" lodash "^4.11.1" memory-fs "^0.4.1" minimatch "^3.0.4" @@ -75,111 +78,108 @@ nopt "^4.0.1" opn "~5.1.0" portfinder "~1.0.12" - postcss-custom-properties "^6.1.0" - postcss-loader "^2.0.8" + postcss-import "^11.0.0" + postcss-loader "^2.0.10" postcss-url "^7.1.2" raw-loader "^0.5.1" resolve "^1.1.7" - rxjs "^5.5.2" - sass-loader "^6.0.3" + rxjs "^5.5.6" + sass-loader "^6.0.6" semver "^5.1.0" silent-error "^1.0.0" - source-map-loader "^0.2.0" source-map-support "^0.4.1" style-loader "^0.13.1" stylus "^0.54.5" stylus-loader "^3.0.1" - uglifyjs-webpack-plugin "1.0.0" + uglifyjs-webpack-plugin "^1.1.5" url-loader "^0.6.2" - webpack "~3.8.1" - webpack-concat-plugin "1.4.0" + webpack "~3.10.0" webpack-dev-middleware "~1.12.0" - webpack-dev-server "~2.9.3" + webpack-dev-server "~2.11.0" webpack-merge "^4.1.0" webpack-sources "^1.0.0" webpack-subresource-integrity "^1.0.1" - zone.js "^0.8.14" optionalDependencies: - node-sass "^4.3.0" + node-sass "^4.7.2" -"@angular/common@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.0.1.tgz#43005ab3c8b8ffaf176aafb3b86ba931c3e4bdf9" +"@angular/common@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.2.3.tgz#f2a5c12ec44b73a248d8dcb2d37ac2749f091cf7" dependencies: tslib "^1.7.1" -"@angular/compiler-cli@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.0.1.tgz#526dc1bb394fb16ad916601eea9aa00eb44b4fff" +"@angular/compiler-cli@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.2.3.tgz#75b39a346f1b2a95ebc403016d19fc33d5315221" dependencies: chokidar "^1.4.2" minimist "^1.2.0" reflect-metadata "^0.1.2" - tsickle "^0.24.0" + tsickle "^0.26.0" -"@angular/compiler@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.0.1.tgz#7fd4c7fa4bbbef4c146962fa946b827330a6c8ed" +"@angular/compiler@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.2.3.tgz#49167adb35bdb5e7e915cce912c432300fc16816" dependencies: tslib "^1.7.1" -"@angular/core@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.0.1.tgz#a4a74afc7e2058d30b8263eb6d66daace9f427ba" +"@angular/core@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.2.3.tgz#157dcb437d085b325513689e0e4ae70182f3f708" dependencies: tslib "^1.7.1" -"@angular/flex-layout@2.0.0-beta.10-4905443": - version "2.0.0-beta.10-4905443" - resolved "https://registry.yarnpkg.com/@angular/flex-layout/-/flex-layout-2.0.0-beta.10-4905443.tgz#c62915c8f9a379d1da4800c12e3751e32cc7f855" +"@angular/flex-layout@2.0.0-beta.12": + version "2.0.0-beta.12" + resolved "https://registry.yarnpkg.com/@angular/flex-layout/-/flex-layout-2.0.0-beta.12.tgz#80970dc1d60f27fa41537659926f3238f759f343" dependencies: tslib "^1.7.1" -"@angular/forms@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.0.1.tgz#69f303c4c13da3caa0de63437588388b6ad62b21" +"@angular/forms@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.2.3.tgz#4992419191a3800516085209be331fc0d63bd099" dependencies: tslib "^1.7.1" -"@angular/http@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.0.1.tgz#350cbdf63cfac8939613d753ff071ed58a60561b" +"@angular/http@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.2.3.tgz#067945f6339619b0b3fd13e8af7b6a1301e69176" dependencies: tslib "^1.7.1" -"@angular/language-service@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-5.0.1.tgz#869e09dbd6e3d95c117c062d21dd1fd920ad44d6" +"@angular/language-service@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-5.2.3.tgz#b858c1cd3740b29de145a6d60b2562eef391d3b3" -"@angular/material@5.0.0-rc.2": - version "5.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@angular/material/-/material-5.0.0-rc.2.tgz#c96bb3295787aa84aa3788d776e15611f77a70ed" +"@angular/material@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@angular/material/-/material-5.2.0.tgz#8599e3149d48487e3e92e941fa9dc55176e3a0cf" dependencies: tslib "^1.7.1" -"@angular/platform-browser-dynamic@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.0.1.tgz#16db67d52d4531563ab15429c6bdfe18bc1bedc8" +"@angular/platform-browser-dynamic@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.3.tgz#87d5172da2c102c3cf48da29d39e9b4b9414b419" dependencies: tslib "^1.7.1" -"@angular/platform-browser@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.0.1.tgz#14895dd30ed2a30ee7b99c76b764748f46c1a862" +"@angular/platform-browser@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.2.3.tgz#953df896839879e2b8ca89a364d66e3ac1dcd167" dependencies: tslib "^1.7.1" -"@angular/platform-server@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.0.1.tgz#005a64eb657e670d265fdfae9473f19a764f8737" +"@angular/platform-server@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.2.3.tgz#bc10c5b4789fe26f2e677aae069f3ff7509821cb" dependencies: domino "^1.0.29" tslib "^1.7.1" xhr2 "^0.1.4" -"@angular/router@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.0.1.tgz#9ac08f29302ef60cdfd3c7810d96c265dec463d6" +"@angular/router@^5.2.0": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.2.3.tgz#c56bce42817c67aeb65291e5adf293300038069e" dependencies: tslib "^1.7.1" @@ -187,9 +187,9 @@ version "1.1.0" resolved "https://registry.yarnpkg.com/@ngtools/json-schema/-/json-schema-1.1.0.tgz#c3a0c544d62392acc2813a42c8a0dc6f58f86922" -"@ngtools/webpack@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.8.5.tgz#f35b646f58db1d4ac5938031051a0f9187d91961" +"@ngtools/webpack@1.9.7": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.9.7.tgz#ef15b90142ddf2a2c9072fe3d58c6bf500163fe5" dependencies: chalk "~2.2.0" enhanced-resolve "^3.1.0" @@ -198,16 +198,17 @@ semver "^5.3.0" source-map "^0.5.6" tree-kill "^1.0.0" + webpack-sources "^1.1.0" -"@ngx-translate/core@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.0.1.tgz#000f2d863c4c94c818e1416ef43cca2c5c0c5848" +"@ngx-translate/core@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.1.1.tgz#ae103928836b8a9e069fd2e2e76fa2198cc7e628" -"@schematics/angular@~0.1.8": - version "0.1.9" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.9.tgz#f4bd5da3da0e9414db7ee2a967a4789959c47a24" +"@schematics/angular@0.1.17": + version "0.1.17" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.17.tgz#084a7cbe2de6f94a856bd08d95c9d35ef8905e2b" dependencies: - "@angular-devkit/core" "0.0.22" + typescript "~2.6.2" "@types/d3-array@*": version "1.2.1" @@ -389,9 +390,9 @@ version "1.0.3" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.3.tgz#fbcf7fa5eb6dd108d51385cc6987ec1f24214523" -"@types/jasmine@*", "@types/jasmine@~2.5.53": - version "2.5.54" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.54.tgz#a6b5f2ae2afb6e0307774e8c7c608e037d491c63" +"@types/jasmine@*", "@types/jasmine@~2.8.3": + version "2.8.6" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e" "@types/jasminewd2@~2.0.2": version "2.0.3" @@ -411,6 +412,21 @@ version "2.53.42" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz#74cb77fb6052edaff2a8984ddafd88d419f25cac" +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + +JSONStream@^1.0.3: + version "1.3.2" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + abbrev@1: version "1.1.0" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" @@ -422,6 +438,13 @@ accepts@1.3.3, accepts@~1.3.3: mime-types "~2.1.11" negotiator "0.6.1" +accepts@~1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" + dependencies: + mime-types "~2.1.16" + negotiator "0.6.1" + acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -436,6 +459,14 @@ acorn@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7" +acorn@^5.2.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" + +addressparser@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" + adm-zip@0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.4.tgz#a61ed5ae6905c3aea58b3a657d25033091052736" @@ -455,7 +486,7 @@ agent-base@2: extend "~3.0.0" semver "~5.0.1" -ajv-keywords@^2.0.0: +ajv-keywords@^2.0.0, ajv-keywords@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0" @@ -466,14 +497,14 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.0.0, ajv@^5.1.5: - version "5.2.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" +ajv@^5.0.0, ajv@^5.1.5, ajv@~5.5.1: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" - json-stable-stringify "^1.0.1" align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" @@ -491,9 +522,19 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -angular2-toaster@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/angular2-toaster/-/angular2-toaster-4.0.1.tgz#587aef6df903cf5e2b7eba4047b54ec9a21b8e3b" +amqplib@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63" + dependencies: + bitsyntax "~0.0.4" + bluebird "^3.4.6" + buffer-more-ints "0.0.2" + readable-stream "1.x >=1.1.9" + safe-buffer "^5.0.1" + +angular2-toaster@5.0.0-alpha.1: + version "5.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/angular2-toaster/-/angular2-toaster-5.0.0-alpha.1.tgz#24b5435c8c4ab5180c6071f9939ee13f5532428c" angular2-uuid@^1.1.1: version "1.1.1" @@ -528,6 +569,13 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + app-root-path@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46" @@ -538,14 +586,10 @@ append-transform@^0.4.0: dependencies: default-require-extensions "^1.0.0" -aproba@^1.0.3: +aproba@^1.0.3, aproba@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" @@ -565,10 +609,22 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -588,6 +644,14 @@ array-includes@^3.0.3: define-properties "^1.1.2" es-abstract "^1.7.0" +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + array-slice@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" @@ -606,9 +670,13 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -arraybuffer.slice@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" arrify@^1.0.0: version "1.0.1" @@ -638,12 +706,26 @@ assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" -assert@^1.1.1: +assert@^1.1.1, assert@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" dependencies: util "0.10.3" +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +ast-types@0.x.x: + version "0.10.1" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" + +astw@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917" + dependencies: + acorn "^4.0.3" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -652,9 +734,9 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" -async@^0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" async@^1.4.0, async@^1.5.2: version "1.5.2" @@ -666,11 +748,21 @@ async@^2.1.2, async@^2.1.4, async@^2.1.5, async@^2.4.1: dependencies: lodash "^4.14.0" +async@~2.1.2: + version "2.1.5" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" -autoprefixer@^6.3.1, autoprefixer@^6.5.3: +atob@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + +autoprefixer@^6.3.1: version "6.7.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" dependencies: @@ -681,6 +773,17 @@ autoprefixer@^6.3.1, autoprefixer@^6.5.3: postcss "^5.2.16" postcss-value-parser "^3.2.3" +autoprefixer@^7.2.3: + version "7.2.5" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.2.5.tgz#04ccbd0c6a61131b6d13f53d371926092952d192" + dependencies: + browserslist "^2.11.1" + caniuse-lite "^1.0.30000791" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^6.0.16" + postcss-value-parser "^3.2.3" + aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" @@ -689,6 +792,12 @@ aws4@^1.2.1: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +axios@^0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053" + dependencies: + follow-redirects "1.0.0" + babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -784,6 +893,18 @@ base64id@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -808,6 +929,18 @@ binary-extensions@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" +bitsyntax@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82" + dependencies: + buffer-more-ints "0.0.2" + +bl@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" + dependencies: + readable-stream "~2.0.5" + blob@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" @@ -824,11 +957,11 @@ blocking-proxy@0.0.5: dependencies: minimist "^1.2.0" -bluebird@^3.3.0, bluebird@^3.4.7: +bluebird@^3.3.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" -bluebird@^3.5.0, bluebird@^3.5.1: +bluebird@^3.4.6, bluebird@^3.4.7, bluebird@^3.5.0, bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -836,6 +969,21 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" +body-parser@1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + body-parser@^1.16.1: version "1.17.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee" @@ -893,10 +1041,43 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.0.tgz#a46941cb5fb492156b3d6a656e06c35364e3e66e" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + define-property "^1.0.0" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" +browser-pack@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.3.tgz#91ca96518583ef580ab063a309de62e407767a39" + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.8.0" + defined "^1.0.0" + safe-buffer "^5.1.1" + through2 "^2.0.0" + umd "^3.0.0" + +browser-resolve@^1.11.0, browser-resolve@^1.7.0: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + dependencies: + resolve "1.1.7" + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.0.8" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.8.tgz#c8fa3b1b7585bb7ba77c5560b60996ddec6d5309" @@ -949,6 +1130,64 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" +browserify-zlib@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserify@^14.5.0: + version "14.5.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5" + dependencies: + JSONStream "^1.0.3" + assert "^1.4.0" + browser-pack "^6.0.1" + browser-resolve "^1.11.0" + browserify-zlib "~0.2.0" + buffer "^5.0.2" + cached-path-relative "^1.0.0" + concat-stream "~1.5.1" + console-browserify "^1.1.0" + constants-browserify "~1.0.0" + crypto-browserify "^3.0.0" + defined "^1.0.0" + deps-sort "^2.0.0" + domain-browser "~1.1.0" + duplexer2 "~0.1.2" + events "~1.1.0" + glob "^7.1.0" + has "^1.0.0" + htmlescape "^1.1.0" + https-browserify "^1.0.0" + inherits "~2.0.1" + insert-module-globals "^7.0.0" + labeled-stream-splicer "^2.0.0" + module-deps "^4.0.8" + os-browserify "~0.3.0" + parents "^1.0.1" + path-browserify "~0.0.0" + process "~0.11.0" + punycode "^1.3.2" + querystring-es3 "~0.2.0" + read-only-stream "^2.0.0" + readable-stream "^2.0.2" + resolve "^1.1.4" + shasum "^1.0.0" + shell-quote "^1.6.1" + stream-browserify "^2.0.0" + stream-http "^2.0.0" + string_decoder "~1.0.0" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^2.0.0" + timers-browserify "^1.0.1" + tty-browserify "~0.0.0" + url "~0.11.0" + util "~0.10.1" + vm-browserify "~0.0.1" + xtend "^4.0.0" + browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: version "1.7.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" @@ -956,10 +1195,21 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" +browserslist@^2.11.1: + version "2.11.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.3.tgz#fe36167aed1bbcde4827ebfe71347a2cc70b99b2" + dependencies: + caniuse-lite "^1.0.30000792" + electron-to-chromium "^1.3.30" + buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" +buffer-more-ints@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c" + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -972,7 +1222,26 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.0.0: +buffer@^5.0.2: + version "5.0.8" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +buildmail@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72" + dependencies: + addressparser "1.0.1" + libbase64 "0.1.0" + libmime "3.0.0" + libqp "1.1.0" + nodemailer-fetch "1.6.0" + nodemailer-shared "1.1.0" + punycode "1.4.1" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -988,9 +1257,13 @@ bytes@2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a" -cacache@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.1.tgz#3e05f6e616117d9b54665b1b20c8aeb93ea5d36f" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + +cacache@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.2.tgz#105a93a162bbedf3a25da42e1939ed99ffb145f8" dependencies: bluebird "^3.5.0" chownr "^1.0.1" @@ -1006,6 +1279,24 @@ cacache@^10.0.0: unique-filename "^1.1.0" y18n "^3.2.1" +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cached-path-relative@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7" + callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" @@ -1053,6 +1344,14 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000726" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000726.tgz#9bb742f8d026a62df873bc03c06843d2255b60d7" +caniuse-lite@^1.0.30000791, caniuse-lite@^1.0.30000792: + version "1.0.30000803" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000803.tgz#9939c37149d38d5f4540430490d240c03106a0f5" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1074,15 +1373,7 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" - dependencies: - ansi-styles "^3.1.0" - escape-string-regexp "^1.0.5" - supports-color "^4.0.0" - -chalk@^2.3.0: +chalk@^2.0.0, chalk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" dependencies: @@ -1098,24 +1389,13 @@ chalk@~2.2.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - -chart.js@2.7.1: +chart.js@2.7.1, chart.js@^2.6.0: version "2.7.1" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.7.1.tgz#ae90b4aa4ff1f02decd6b1a2a8dabfd73c9f9886" dependencies: chartjs-color "~2.2.0" moment "~2.18.0" -chart.js@^2.6.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.7.0.tgz#330dbbee0c66199eb715f60fbf8ca4029609a529" - dependencies: - chartjs-color "~2.2.0" - moment "~2.18.0" - chartjs-color-string@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz#8d3752d8581d86687c35bfe2cb80ac5213ceb8c1" @@ -1129,7 +1409,7 @@ chartjs-color@~2.2.0: chartjs-color-string "^0.5.0" color-convert "^0.5.3" -chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.6.0, chokidar@^1.7.0: +chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: @@ -1144,6 +1424,23 @@ chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.6.0, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +chokidar@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.0.tgz#6686313c541d3274b2a5c01233342037948c911b" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" @@ -1155,9 +1452,13 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-dependency-plugin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-3.0.0.tgz#9b68692e35b0e3510998d0164b6ae5011bea5760" +circular-dependency-plugin@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-4.4.0.tgz#f8a1a746a3f6c8e57f4dae9b54d991cd2a582f5d" + +circular-json@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f" clap@^1.0.9: version "1.2.0" @@ -1165,6 +1466,15 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + classlist.js@^1.1.20150312: version "1.1.20150312" resolved "https://registry.yarnpkg.com/classlist.js/-/classlist.js-1.1.20150312.tgz#1d70842f7022f08d9ac086ce69e5b250f2c57789" @@ -1212,6 +1522,10 @@ co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +co@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda" + coa@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" @@ -1233,6 +1547,13 @@ codelyzer@^4.0.1: source-map "^0.5.6" sprintf-js "^1.0.3" +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" @@ -1269,7 +1590,7 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" -colors@1.1.2, colors@^1.1.0, colors@^1.1.2, colors@~1.1.2: +colors@1.1.2, colors@^1.1.0, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" @@ -1279,16 +1600,42 @@ combine-lists@^1.0.0: dependencies: lodash "^4.5.0" +combine-source-map@~0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" + +combine-source-map@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" dependencies: delayed-stream "~1.0.0" -commander@2, commander@2.11.x, commander@^2.9.0, commander@~2.11.0: +commander@2, commander@^2.12.1, commander@^2.9.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.0.tgz#7b25325963e6aace20d3a9285b09379b0c2208b5" + +commander@2.11.x, commander@~2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + common-tags@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" @@ -1303,11 +1650,7 @@ component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" -component-emitter@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" - -component-emitter@1.2.1: +component-emitter@1.2.1, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -1337,13 +1680,13 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" +concat-stream@^1.5.0, concat-stream@~1.5.0, concat-stream@~1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" connect-history-api-fallback@^1.3.0: version "1.3.0" @@ -1368,7 +1711,7 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -constants-browserify@^1.0.0: +constants-browserify@^1.0.0, constants-browserify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -1380,9 +1723,17 @@ content-type@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" -convert-source-map@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +convert-source-map@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +convert-source-map@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" cookie-signature@1.0.6: version "1.0.6" @@ -1403,6 +1754,10 @@ copy-concurrently@^1.0.0: rimraf "^2.5.4" run-queue "^1.0.0" +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + copy-webpack-plugin@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.2.0.tgz#252bb94597f96399d23d7fad355f8d3a661ac096" @@ -1484,17 +1839,13 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" dependencies: boom "2.x.x" -crypto-browserify@^3.11.0: +crypto-browserify@^3.0.0, crypto-browserify@^3.11.0: version "3.11.1" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f" dependencies: @@ -1629,11 +1980,7 @@ cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" -d3-array@1, d3-array@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.0.tgz#147d269720e174c4057a7f42be8b0f3f2ba53108" - -d3-array@1.2.1: +d3-array@1, d3-array@1.2.1, d3-array@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc" @@ -1670,29 +2017,14 @@ d3-dispatch@1, d3-dispatch@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8" -d3-drag@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.1.1.tgz#b5155304433b18ba38726b2184d0098e820dc64b" - dependencies: - d3-dispatch "1" - d3-selection "1" - -d3-drag@1.2.1: +d3-drag@1, d3-drag@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.1.tgz#df8dd4c502fb490fc7462046a8ad98a5c479282d" dependencies: d3-dispatch "1" d3-selection "1" -d3-dsv@1: - version "1.0.7" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.7.tgz#137076663f398428fc3d031ae65370522492b78f" - dependencies: - commander "2" - iconv-lite "0.4" - rw "1" - -d3-dsv@1.0.8: +d3-dsv@1, d3-dsv@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.8.tgz#907e240d57b386618dc56468bacfe76bf19764ae" dependencies: @@ -1713,17 +2045,13 @@ d3-force@1.1.0: d3-quadtree "1" d3-timer "1" -d3-format@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.0.tgz#6b480baa886885d4651dc248a8f4ac9da16db07a" - -d3-format@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f" +d3-format@1, d3-format@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a" -d3-geo@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.0.tgz#15c7d7a8ea9346e59ed150dc7b1f7f95479056e9" +d3-geo@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356" dependencies: d3-array "1" @@ -1731,13 +2059,7 @@ d3-hierarchy@1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26" -d3-interpolate@1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.5.tgz#69e099ff39214716e563c9aec3ea9d1ea4b8a79f" - dependencies: - d3-color "1" - -d3-interpolate@1.1.6: +d3-interpolate@1, d3-interpolate@1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6" dependencies: @@ -1784,13 +2106,20 @@ d3-scale@1.0.7: d3-time "1" d3-time-format "2" -d3-selection@1, d3-selection@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.1.0.tgz#1998684896488f839ca0372123da34f1d318809c" +d3-scale@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.0.0.tgz#fd8ac78381bc2ed741d8c71770437a5e0549a5a5" + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" -d3-selection@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88" +d3-selection@1, d3-selection@1.3.0, d3-selection@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.0.tgz#d53772382d3dc4f7507bfb28bcd2d6aed2a0ad6d" d3-shape@1.2.0: version "1.2.0" @@ -1798,23 +2127,13 @@ d3-shape@1.2.0: dependencies: d3-path "1" -d3-time-format@2: - version "2.0.5" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.0.5.tgz#9d7780204f7c9119c9170b1a56db4de9a8af972e" - dependencies: - d3-time "1" - -d3-time-format@2.1.1: +d3-time-format@2, d3-time-format@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31" dependencies: d3-time "1" -d3-time@1: - version "1.0.7" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.7.tgz#94caf6edbb7879bb809d0d1f7572bc48482f7270" - -d3-time@1.0.8: +d3-time@1, d3-time@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84" @@ -1822,18 +2141,7 @@ d3-timer@1, d3-timer@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531" -d3-transition@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.0.tgz#cfc85c74e5239324290546623572990560c3966f" - dependencies: - d3-color "1" - d3-dispatch "1" - d3-ease "1" - d3-interpolate "1" - d3-selection "^1.1.0" - d3-timer "1" - -d3-transition@1.1.1: +d3-transition@1, d3-transition@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.1.tgz#d8ef89c3b848735b060e54a39b32aaebaa421039" dependencies: @@ -1858,9 +2166,9 @@ d3-zoom@1.7.1: d3-selection "1" d3-transition "1" -d3@4.12.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.0.tgz#75eccb39ea40f6018de8cfa2752905bee7daa46f" +d3@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-4.13.0.tgz#ab236ff8cf0cfc27a81e69bf2fb7518bc9b4f33d" dependencies: d3-array "1.2.1" d3-axis "1.0.8" @@ -1873,8 +2181,8 @@ d3@4.12.0: d3-dsv "1.0.8" d3-ease "1.0.3" d3-force "1.1.0" - d3-format "1.2.1" - d3-geo "1.9.0" + d3-format "1.2.2" + d3-geo "1.9.1" d3-hierarchy "1.1.5" d3-interpolate "1.1.6" d3-path "1.0.5" @@ -1884,7 +2192,7 @@ d3@4.12.0: d3-random "1.1.0" d3-request "1.0.6" d3-scale "1.0.7" - d3-selection "1.2.0" + d3-selection "1.3.0" d3-shape "1.2.0" d3-time "1.0.8" d3-time-format "2.1.1" @@ -1905,31 +2213,33 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + date-fns@^2.0.0-alpha.7: version "2.0.0-alpha.7" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.7.tgz#245ad16f95764eababfb2c0a41fd5d033c20e57a" +date-format@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8" + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@*, debug@2, debug@2.6.8, debug@^2.2.0, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8: +debug@*, debug@2.6.8, debug@^2.2.0, debug@~2.6.6: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" +debug@2, debug@2.6.9, debug@^2.3.3, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8, debug@~2.6.4, debug@~2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: - ms "0.7.1" - -debug@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" - dependencies: - ms "0.7.2" + ms "2.0.0" debug@2.6.7: version "2.6.7" @@ -1943,10 +2253,20 @@ debug@^3.1.0: dependencies: ms "2.0.0" +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -1955,6 +2275,10 @@ deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + default-require-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" @@ -1968,10 +2292,30 @@ define-properties@^1.1.2: foreach "^2.0.5" object-keys "^1.0.8" +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" +degenerator@~1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + del@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -2011,6 +2355,15 @@ depd@1.1.1, depd@~1.1.0, depd@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" +deps-sort@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5" + dependencies: + JSONStream "^1.0.3" + shasum "^1.0.0" + subarg "^1.0.0" + through2 "^2.0.0" + des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" @@ -2032,6 +2385,13 @@ detect-node@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" +detective@^4.0.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" + dependencies: + acorn "^5.2.1" + defined "^1.0.0" + di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" @@ -2087,7 +2447,7 @@ dom-serializer@0: domelementtype "~1.1.1" entities "~1.1.1" -domain-browser@^1.1.1: +domain-browser@^1.1.1, domain-browser@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" @@ -2122,6 +2482,16 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" +double-ended-queue@^2.1.0-0: + version "2.1.0-0" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + +duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + dependencies: + readable-stream "^2.0.2" + duplexify@^3.1.2, duplexify@^3.4.2: version "3.5.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd" @@ -2145,9 +2515,9 @@ ejs@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" -electron-to-chromium@^1.2.7: - version "1.3.21" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2" +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: + version "1.3.32" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.32.tgz#11d0684c0840e003c4be8928f8ac5f35dbc2b4e6" elliptic@^6.0.0: version "6.4.0" @@ -2179,44 +2549,44 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -engine.io-client@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" +engine.io-client@~3.1.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.4.tgz#4fcf1370b47163bd2ce9be2733972430350d4ea1" dependencies: component-emitter "1.2.1" component-inherit "0.0.3" - debug "2.3.3" - engine.io-parser "1.3.2" + debug "~2.6.9" + engine.io-parser "~2.1.1" has-cors "1.1.0" indexof "0.0.1" - parsejson "0.0.3" parseqs "0.0.5" parseuri "0.0.5" - ws "1.1.2" - xmlhttprequest-ssl "1.5.3" + ws "~3.3.1" + xmlhttprequest-ssl "~1.5.4" yeast "0.1.2" -engine.io-parser@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a" +engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196" dependencies: after "0.8.2" - arraybuffer.slice "0.0.6" + arraybuffer.slice "~0.0.7" base64-arraybuffer "0.1.5" blob "0.0.4" - has-binary "0.1.7" - wtf-8 "1.0.0" + has-binary2 "~1.0.2" -engine.io@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4" +engine.io@~3.1.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.4.tgz#3d0211b70a552ce841ffc7da8627b301a9a4162e" dependencies: accepts "1.3.3" base64id "1.0.0" cookie "0.3.1" - debug "2.3.3" - engine.io-parser "1.3.2" - ws "1.1.2" + debug "~2.6.9" + engine.io-parser "~2.1.0" + ws "~3.3.1" + optionalDependencies: + uws "~0.14.4" enhanced-resolve@^3.1.0, enhanced-resolve@^3.4.0: version "3.4.1" @@ -2325,6 +2695,17 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escodegen@1.x.x: + version "1.9.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.5.6" + escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" @@ -2334,6 +2715,10 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +esprima@3.x.x, esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -2349,7 +2734,7 @@ esrecurse@^4.1.0: estraverse "^4.1.0" object-assign "^4.0.1" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" @@ -2357,9 +2742,9 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" -etag@~1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" event-emitter@~0.3.5: version "0.3.5" @@ -2372,7 +2757,7 @@ eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" -events@^1.0.0: +events@^1.0.0, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -2419,6 +2804,18 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" @@ -2439,38 +2836,53 @@ exports-loader@^0.6.3: loader-utils "^1.0.2" source-map "0.5.x" -express@^4.13.3: - version "4.15.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1" +express@^4.16.2: + version "4.16.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" dependencies: - accepts "~1.3.3" + accepts "~1.3.4" array-flatten "1.1.1" + body-parser "1.18.2" content-disposition "0.5.2" - content-type "~1.0.2" + content-type "~1.0.4" cookie "0.3.1" cookie-signature "1.0.6" - debug "2.6.8" + debug "2.6.9" depd "~1.1.1" encodeurl "~1.0.1" escape-html "~1.0.3" - etag "~1.8.0" - finalhandler "~1.0.4" - fresh "0.5.0" + etag "~1.8.1" + finalhandler "1.1.0" + fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.1" + parseurl "~1.3.2" path-to-regexp "0.1.7" - proxy-addr "~1.1.5" - qs "6.5.0" + proxy-addr "~2.0.2" + qs "6.5.1" range-parser "~1.2.0" - send "0.15.4" - serve-static "1.12.4" - setprototypeof "1.0.3" + safe-buffer "5.1.1" + send "0.16.1" + serve-static "1.13.1" + setprototypeof "1.1.0" statuses "~1.3.1" type-is "~1.6.15" - utils-merge "1.0.0" - vary "~1.1.1" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" extend@3, extend@^3.0.0, extend@~3.0.0: version "3.0.1" @@ -2482,9 +2894,22 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" -extract-text-webpack-plugin@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.0.tgz#90caa7907bc449f335005e3ac7532b41b00de612" +extglob@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-text-webpack-plugin@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz#5f043eaa02f9750a9258b78c0a6e0dc1408fb2f7" dependencies: async "^2.4.1" loader-utils "^1.1.0" @@ -2499,6 +2924,14 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" @@ -2522,6 +2955,10 @@ file-loader@^1.1.5: loader-utils "^1.0.2" schema-utils "^0.3.0" +file-uri-to-path@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -2543,7 +2980,16 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@1.0.4, finalhandler@~1.0.4: +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7" dependencies: @@ -2555,6 +3001,18 @@ finalhandler@1.0.4, finalhandler@~1.0.4: statuses "~1.3.1" unpipe "~1.0.0" +finalhandler@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + dependencies: + debug "2.6.9" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.3.1" + unpipe "~1.0.0" + find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -2587,11 +3045,17 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37" + dependencies: + debug "^2.2.0" + for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" -for-in@^1.0.1: +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2615,6 +3079,14 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" +form-data@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.11" + form-data@~2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" @@ -2623,13 +3095,19 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" -fresh@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" from2@^2.1.0: version "2.3.0" @@ -2697,6 +3175,13 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2720,6 +3205,16 @@ gaze@^1.0.0: dependencies: globule "^1.0.0" +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -2732,6 +3227,21 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-uri@2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59" + dependencies: + data-uri-to-buffer "1" + debug "2" + extend "3" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "2" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2751,6 +3261,13 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + glob@7.0.x: version "7.0.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" @@ -2762,7 +3279,17 @@ glob@7.0.x: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -2832,6 +3359,15 @@ har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + har-validator@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" @@ -2845,11 +3381,11 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-binary@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c" +has-binary2@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98" dependencies: - isarray "0.0.1" + isarray "2.0.1" has-cors@1.1.0: version "1.1.0" @@ -2867,7 +3403,34 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.1: +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" dependencies: @@ -2906,6 +3469,13 @@ he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +hipchat-notifier@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e" + dependencies: + lodash "^4.0.0" + request "^2.0.0" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -2963,6 +3533,10 @@ html-webpack-plugin@^2.29.0: pretty-error "^2.0.2" toposort "^1.0.0" +htmlescape@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" + htmlparser2@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" @@ -2976,7 +3550,7 @@ http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" -http-errors@~1.6.1, http-errors@~1.6.2: +http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: @@ -2985,6 +3559,14 @@ http-errors@~1.6.1, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" +http-proxy-agent@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" + dependencies: + agent-base "2" + debug "2" + extend "3" + http-proxy-middleware@~0.17.4: version "0.17.4" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" @@ -3009,11 +3591,26 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" +httpntlm@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2" + dependencies: + httpreq ">=0.4.22" + underscore "~1.7.0" + +httpreq@>=0.4.22: + version "0.4.24" + resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-0.4.24.tgz#4335ffd82cd969668a39465c929ac61d6393627f" + https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -https-proxy-agent@^1.0.0: +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +https-proxy-agent@1, https-proxy-agent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" dependencies: @@ -3025,6 +3622,10 @@ iconv-lite@0.4, iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -3047,9 +3648,9 @@ image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" -import-local@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-0.1.1.tgz#b1179572aacdc11c6a91009fb430dbcab5f668a8" +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" dependencies: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" @@ -3076,6 +3677,14 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" +inflection@~1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" + +inflection@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.3.8.tgz#cbd160da9f75b14c3cc63578d4f396784bf3014e" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3095,6 +3704,25 @@ ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" +inline-source-map@~0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" + dependencies: + source-map "~0.5.3" + +insert-module-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3" + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.7.1" + concat-stream "~1.5.1" + is-buffer "^1.1.0" + lexical-scope "^1.2.0" + process "~0.11.0" + through2 "^2.0.0" + xtend "^4.0.0" + internal-ip@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" @@ -3119,18 +3747,34 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -ip@^1.1.0, ip@^1.1.5: +ip@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590" + +ip@^1.1.0, ip@^1.1.2, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" -ipaddr.js@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" +ipaddr.js@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3141,7 +3785,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1: +is-buffer@^1.0.2, is-buffer@^1.1.0, is-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" @@ -3155,10 +3799,38 @@ is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" @@ -3173,10 +3845,16 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" @@ -3219,6 +3897,15 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-my-json-valid@^2.12.4: + version "2.17.1" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz#3da98914a70a22f0a8563ef1511a246c6fc55471" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + is-number@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" @@ -3235,6 +3922,12 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-odd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-1.0.0.tgz#3b8a932eb028b3775c39bb09e91767accdb69088" + dependencies: + is-number "^3.0.0" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -3255,7 +3948,7 @@ is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-object@^2.0.1: +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: @@ -3269,6 +3962,10 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -3301,7 +3998,7 @@ is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" -isarray@0.0.1: +isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -3309,6 +4006,10 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -3323,7 +4024,7 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.1: +isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -3347,14 +4048,14 @@ istanbul-api@^1.1.8: mkdirp "^0.5.1" once "^1.4.0" -istanbul-instrumenter-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz#e5492900ab0bba835efa8024cb00be9b3eea2700" +istanbul-instrumenter-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz#9f553923b22360bac95e617aaba01add1f7db0b2" dependencies: - convert-source-map "^1.3.0" - istanbul-lib-instrument "^1.1.3" - loader-utils "^0.2.16" - object-assign "^4.1.0" + convert-source-map "^1.5.0" + istanbul-lib-instrument "^1.7.3" + loader-utils "^1.1.0" + schema-utils "^0.3.0" istanbul-lib-coverage@^1.1.1: version "1.1.1" @@ -3366,7 +4067,7 @@ istanbul-lib-hook@^1.0.7: dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.1.3, istanbul-lib-instrument@^1.8.0: +istanbul-lib-instrument@^1.7.3, istanbul-lib-instrument@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532" dependencies: @@ -3403,17 +4104,13 @@ istanbul-reports@^1.1.2: dependencies: handlebars "^4.0.3" -jasmine-core@~2.6.2: - version "2.6.4" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.4.tgz#dec926cd0a9fa287fb6db5c755fa487e74cecac5" - jasmine-core@~2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" -jasmine-spec-reporter@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.1.1.tgz#5a6d58ab5d61bea7309fbc279239511756b1b588" +jasmine-spec-reporter@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz#1d632aec0341670ad324f92ba84b4b32b35e9e22" dependencies: colors "1.1.2" @@ -3481,11 +4178,17 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stable-stringify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json3@3.3.2, json3@^3.3.2: +json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -3509,6 +4212,14 @@ jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -3518,19 +4229,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -karma-chrome-launcher@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf" +karma-chrome-launcher@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf" dependencies: fs-access "^1.0.0" which "^1.2.1" -karma-cli@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/karma-cli/-/karma-cli-1.0.1.tgz#ae6c3c58a313a1d00b45164c455b9b86ce17f960" - dependencies: - resolve "^1.1.6" - karma-coverage-istanbul-reporter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.3.0.tgz#d142cd9c55731c9e363ef7374e8ef1a31bebfadb" @@ -3554,12 +4259,13 @@ karma-source-map-support@^1.2.0: dependencies: source-map-support "^0.4.1" -karma@~1.7.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.1.tgz#85cc08e9e0a22d7ce9cca37c4a1be824f6a2b1ae" +karma@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.0.tgz#a02698dd7f0f05ff5eb66ab8f65582490b512e58" dependencies: bluebird "^3.3.0" body-parser "^1.16.1" + browserify "^14.5.0" chokidar "^1.4.1" colors "^1.1.0" combine-lists "^1.0.0" @@ -3572,8 +4278,8 @@ karma@~1.7.0: graceful-fs "^4.1.2" http-proxy "^1.13.0" isbinaryfile "^3.0.0" - lodash "^3.8.0" - log4js "^0.6.31" + lodash "^4.17.4" + log4js "^2.3.9" mime "^1.3.4" minimatch "^3.0.2" optimist "^0.6.1" @@ -3581,9 +4287,9 @@ karma@~1.7.0: range-parser "^1.2.0" rimraf "^2.6.0" safe-buffer "^5.0.1" - socket.io "1.7.3" - source-map "^0.5.3" - tmp "0.0.31" + socket.io "2.0.4" + source-map "^0.6.1" + tmp "0.0.33" useragent "^2.1.12" killable@^1.0.0: @@ -3596,7 +4302,7 @@ kind-of@^2.0.1: dependencies: is-buffer "^1.0.2" -kind-of@^3.0.2, kind-of@^3.2.2: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0, kind-of@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: @@ -3608,6 +4314,22 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" +kind-of@^5.0.0, kind-of@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +labeled-stream-splicer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59" + dependencies: + inherits "^2.0.1" + isarray "~0.0.1" + stream-splicer "^2.0.0" + lazy-cache@^0.2.3: version "0.2.7" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" @@ -3616,6 +4338,12 @@ lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" +lazy-cache@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" + dependencies: + set-getter "^0.1.0" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -3643,13 +4371,42 @@ less@^2.7.2: request "^2.72.0" source-map "^0.5.3" -license-webpack-plugin@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.0.1.tgz#abeb3ab168a9930f2fd57311951dc094aaf33e45" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" dependencies: - ejs "^2.5.7" + prelude-ls "~1.1.2" + type-check "~0.3.2" -load-json-file@^1.0.0: +lexical-scope@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4" + dependencies: + astw "^2.0.0" + +libbase64@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6" + +libmime@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.0.0.tgz#51a1a9e7448ecbd32cda54421675bb21bc093da6" + dependencies: + iconv-lite "0.4.15" + libbase64 "0.1.0" + libqp "1.1.0" + +libqp@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" + +license-webpack-plugin@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.0.1.tgz#abeb3ab168a9930f2fd57311951dc094aaf33e45" + dependencies: + ejs "^2.5.7" + +load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" dependencies: @@ -3672,22 +4429,22 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@~0.2.2: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" +loader-utils@1.1.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" - object-assign "^4.0.1" -loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" +loader-utils@^0.2.15, loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" + object-assign "^4.0.1" locate-path@^2.0.0: version "2.0.0" @@ -3712,6 +4469,10 @@ lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" +lodash.memoize@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + lodash.mergewith@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" @@ -3724,20 +4485,36 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^3.8.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - -lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.4: +lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" -log4js@^0.6.31: - version "0.6.38" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" +log4js@^2.3.9: + version "2.5.2" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.2.tgz#234e9c688bc4aab3999bd4b149c85851a4e62faa" + dependencies: + circular-json "^0.5.1" + date-format "^1.2.0" + debug "^3.1.0" + semver "^5.3.0" + streamroller "^0.7.0" + optionalDependencies: + amqplib "^0.5.2" + axios "^0.15.3" + hipchat-notifier "^1.1.0" + loggly "^1.1.0" + mailgun-js "^0.7.0" + nodemailer "^2.5.0" + redis "^2.7.1" + slack-node "~0.2.0" + +loggly@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/loggly/-/loggly-1.1.1.tgz#0a0fc1d3fa3a5ec44fdc7b897beba2a4695cebee" dependencies: - readable-stream "~1.0.2" - semver "~4.3.3" + json-stringify-safe "5.0.x" + request "2.75.x" + timespan "2.3.x" loglevel@^1.4.1: version "1.5.0" @@ -3775,6 +4552,10 @@ lru-cache@^4.0.1, lru-cache@^4.1.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@~2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" + macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" @@ -3785,6 +4566,27 @@ magic-string@^0.22.3: dependencies: vlq "^0.2.1" +mailcomposer@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4" + dependencies: + buildmail "4.0.1" + libmime "3.0.0" + +mailgun-js@^0.7.0: + version "0.7.15" + resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb" + dependencies: + async "~2.1.2" + debug "~2.2.0" + form-data "~2.1.1" + inflection "~1.10.0" + is-stream "^1.1.0" + path-proxy "~1.0.0" + proxy-agent "~2.0.0" + q "~1.4.0" + tsscmp "~1.0.0" + make-dir@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" @@ -3795,10 +4597,20 @@ make-error@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + material-design-icons-iconfont@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/material-design-icons-iconfont/-/material-design-icons-iconfont-3.0.3.tgz#154a1084047d4e27237fa7f5a37e1075ceea6df2" @@ -3814,14 +4626,6 @@ md5.js@^1.3.4: hash-base "^3.0.0" inherits "^2.0.1" -md5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -3880,6 +4684,24 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.4: + version "3.1.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.5.tgz#d05e168c206472dfbca985bfef4f57797b4cd4ba" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.0" + define-property "^1.0.0" + extend-shallow "^2.0.1" + extglob "^2.0.2" + fragment-cache "^0.2.1" + kind-of "^6.0.0" + nanomatch "^1.2.5" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + miller-rabin@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" @@ -3891,23 +4713,23 @@ miller-rabin@^4.0.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: +mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: mime-db "~1.30.0" -mime@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +mime@1.4.1, mime@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" mime@^1.2.11, mime@^1.3.4: version "1.4.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.0.tgz#69e9e0db51d44f2a3b56e48b7817d7d137f1a343" -mime@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mime@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" mimic-fn@^1.0.0: version "1.1.0" @@ -3921,7 +4743,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -3931,7 +4753,7 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.1.3, minimist@^1.2.0: +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -3954,6 +4776,13 @@ mississippi@^1.3.0: stream-each "^1.1.0" through2 "^2.0.0" +mixin-deep@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.0.tgz#47a8732ba97799457c8c1eca28f95132d7e8150a" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mixin-object@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" @@ -3967,6 +4796,26 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd dependencies: minimist "0.0.8" +module-deps@^4.0.8: + version "4.1.1" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd" + dependencies: + JSONStream "^1.0.3" + browser-resolve "^1.7.0" + cached-path-relative "^1.0.0" + concat-stream "~1.5.0" + defined "^1.0.0" + detective "^4.0.0" + duplexer2 "^0.1.2" + inherits "^2.0.1" + parents "^1.0.0" + readable-stream "^2.0.2" + resolve "^1.1.3" + stream-combiner2 "^1.1.1" + subarg "^1.0.0" + through2 "^2.0.0" + xtend "^4.0.0" + moment@~2.18.0: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" @@ -3986,10 +4835,6 @@ ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -4005,14 +4850,30 @@ multicast-dns@^6.0.1: dns-packet "^1.0.1" thunky "^0.1.0" -mydaterangepicker@^4.1.12: - version "4.1.12" - resolved "https://registry.yarnpkg.com/mydaterangepicker/-/mydaterangepicker-4.1.12.tgz#0d1e409e992f2b13a5c4f02faf8ea8103841e546" +mydaterangepicker@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/mydaterangepicker/-/mydaterangepicker-4.2.1.tgz#f063e4747016259b9ad882159efc1c7df09221cd" nan@^2.3.0, nan@^2.3.2: version "2.7.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" +nanomatch@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.7.tgz#53cd4aa109ff68b7f869591fdc9d10daeeea3e79" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^1.0.0" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + is-odd "^1.0.0" + kind-of "^5.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + ncname@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" @@ -4023,6 +4884,10 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +netmask@~1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + ng2-charts@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/ng2-charts/-/ng2-charts-1.6.0.tgz#108a2133ff62a8623895240fadbddbea2951f29d" @@ -4033,9 +4898,9 @@ ng2-cookies@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/ng2-cookies/-/ng2-cookies-1.0.12.tgz#3f3e613e0137b0649b705c678074b4bd08149ccc" -ngx-loading@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/ngx-loading/-/ngx-loading-1.0.9.tgz#d0ce8389dabb6b52c3013f1e3d1616b968f0ad1e" +ngx-loading@^1.0.14: + version "1.0.14" + resolved "https://registry.yarnpkg.com/ngx-loading/-/ngx-loading-1.0.14.tgz#19758c33ea3fa9bb96dca1f40ca19d4d86b8042c" no-case@^2.2.0: version "2.3.1" @@ -4117,9 +4982,9 @@ node-pre-gyp@^0.6.36: tar "^2.2.1" tar-pack "^3.4.0" -node-sass@^4.3.0: - version "4.5.3" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" +node-sass@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -4136,9 +5001,63 @@ node-sass@^4.3.0: nan "^2.3.2" node-gyp "^3.3.1" npmlog "^4.0.0" - request "^2.79.0" - sass-graph "^2.1.1" + request "~2.79.0" + sass-graph "^2.2.4" stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +node-uuid@~1.4.7: + version "1.4.8" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + +nodemailer-direct-transport@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86" + dependencies: + nodemailer-shared "1.1.0" + smtp-connection "2.12.0" + +nodemailer-fetch@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4" + +nodemailer-shared@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0" + dependencies: + nodemailer-fetch "1.6.0" + +nodemailer-smtp-pool@2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz#2eb94d6cf85780b1b4725ce853b9cbd5e8da8c72" + dependencies: + nodemailer-shared "1.1.0" + nodemailer-wellknown "0.1.10" + smtp-connection "2.12.0" + +nodemailer-smtp-transport@2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz#03d71c76314f14ac7dbc7bf033a6a6d16d67fb77" + dependencies: + nodemailer-shared "1.1.0" + nodemailer-wellknown "0.1.10" + smtp-connection "2.12.0" + +nodemailer-wellknown@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz#586db8101db30cb4438eb546737a41aad0cf13d5" + +nodemailer@^2.5.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-2.7.2.tgz#f242e649aeeae39b6c7ed740ef7b061c404d30f9" + dependencies: + libmime "3.0.0" + mailcomposer "4.0.1" + nodemailer-direct-transport "3.3.2" + nodemailer-shared "1.1.0" + nodemailer-smtp-pool "2.8.2" + nodemailer-smtp-transport "2.7.2" + socks "1.1.9" "nopt@2 || 3": version "3.0.6" @@ -4162,7 +5081,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1: +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: @@ -4218,7 +5137,7 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@4.1.0, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" @@ -4226,10 +5145,24 @@ object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -4237,6 +5170,12 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + obuf@^1.0.0, obuf@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" @@ -4270,6 +5209,17 @@ optimist@^0.6.1, optimist@~0.6.0: minimist "~0.0.1" wordwrap "~0.0.2" +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" @@ -4284,6 +5234,10 @@ os-browserify@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" +os-browserify@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -4302,7 +5256,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -4331,10 +5285,38 @@ p-map@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a" +pac-proxy-agent@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d" + dependencies: + agent-base "2" + debug "2" + extend "3" + get-uri "2" + http-proxy-agent "1" + https-proxy-agent "1" + pac-resolver "~2.0.0" + raw-body "2" + socks-proxy-agent "2" + +pac-resolver@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd" + dependencies: + co "~3.0.6" + degenerator "~1.0.2" + ip "1.0.1" + netmask "~1.0.4" + thunkify "~2.1.1" + pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + parallel-transform@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" @@ -4349,6 +5331,12 @@ param-case@2.1.x: dependencies: no-case "^2.2.0" +parents@^1.0.0, parents@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" + dependencies: + path-platform "~0.11.15" + parse-asn1@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" @@ -4374,12 +5362,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parsejson@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" - dependencies: - better-assert "~1.0.0" - parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -4396,10 +5378,22 @@ parseurl@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" -path-browserify@0.0.0: +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0, path-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -4426,6 +5420,16 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-platform@~0.11.15: + version "0.11.15" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + +path-proxy@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e" + dependencies: + inflection "~1.3.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4490,6 +5494,10 @@ portfinder@^1.0.9, portfinder@~1.0.12: debug "^2.2.0" mkdirp "0.5.x" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + postcss-calc@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" @@ -4513,13 +5521,6 @@ postcss-convert-values@^2.3.4: postcss "^5.0.11" postcss-value-parser "^3.1.2" -postcss-custom-properties@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-6.2.0.tgz#5d929a7f06e9b84e0f11334194c0ba9a30acfbe9" - dependencies: - balanced-match "^1.0.0" - postcss "^6.0.13" - postcss-discard-comments@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" @@ -4558,6 +5559,15 @@ postcss-filter-plugins@^2.0.0: postcss "^5.0.4" uniqid "^4.0.0" +postcss-import@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-11.0.0.tgz#a962e2df82d3bc5a6da6a386841747204f41ef5b" + dependencies: + postcss "^6.0.1" + postcss-value-parser "^3.2.3" + read-cache "^1.0.0" + resolve "^1.1.7" + postcss-load-config@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" @@ -4581,14 +5591,14 @@ postcss-load-plugins@^2.3.0: cosmiconfig "^2.1.1" object-assign "^4.1.0" -postcss-loader@^2.0.8: - version "2.0.9" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.9.tgz#001fdf7bfeeb159405ee61d1bb8e59b528dbd309" +postcss-loader@^2.0.10: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.0.tgz#038c2d6d59753fef4667827fd3ae03f5dc5e6a7a" dependencies: loader-utils "^1.1.0" postcss "^6.0.0" postcss-load-config "^1.2.0" - schema-utils "^0.3.0" + schema-utils "^0.4.0" postcss-merge-idents@^2.1.5: version "2.1.7" @@ -4777,21 +5787,17 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" -postcss@^6.0.0, postcss@^6.0.13: - version "6.0.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885" +postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.16: + version "6.0.17" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.17.tgz#e259a051ca513f81e9afd0c21f7f82eda50c65c5" dependencies: chalk "^2.3.0" source-map "^0.6.1" - supports-color "^4.4.0" + supports-color "^5.1.0" -postcss@^6.0.1: - version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.11.tgz#f48db210b1d37a7f7ab6499b7a54982997ab6f72" - dependencies: - chalk "^2.1.0" - source-map "^0.5.7" - supports-color "^4.4.0" +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" prepend-http@^1.0.0: version "1.0.4" @@ -4812,7 +5818,7 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -process@^0.11.0: +process@^0.11.0, process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -4846,12 +5852,25 @@ protractor@~5.1.2: webdriver-js-extender "^1.0.0" webdriver-manager "^12.0.6" -proxy-addr@~1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" +proxy-addr@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.5.2" + +proxy-agent@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" dependencies: - forwarded "~0.1.0" - ipaddr.js "1.4.0" + agent-base "2" + debug "2" + extend "3" + http-proxy-agent "1" + https-proxy-agent "1" + lru-cache "~2.6.5" + pac-proxy-agent "1" + socks-proxy-agent "2" prr@~0.0.0: version "0.0.0" @@ -4890,11 +5909,11 @@ punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" -punycode@^1.2.4, punycode@^1.4.1: +punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" -q@1.4.1, q@^1.1.2, q@^1.4.1: +q@1.4.1, q@^1.1.2, q@^1.4.1, q@~1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" @@ -4906,9 +5925,17 @@ qs@6.4.0, qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" -qs@6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" +qs@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +qs@~6.2.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" + +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" query-string@^4.1.0: version "4.3.4" @@ -4917,7 +5944,7 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@^0.2.0: +querystring-es3@^0.2.0, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -4933,12 +5960,6 @@ querystringify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" -queueing-subject@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/queueing-subject/-/queueing-subject-0.1.1.tgz#43f857b2b263276ff418cdd865b7d78d5d641d9d" - dependencies: - rxjs "^5.0.1" - randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -4956,6 +5977,15 @@ range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" +raw-body@2, raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + raw-body@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" @@ -4977,6 +6007,18 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + dependencies: + pify "^2.3.0" + +read-only-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" + dependencies: + readable-stream "^2.0.2" + read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -5007,7 +6049,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9: +"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -5019,7 +6061,7 @@ read-pkg@^2.0.0: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@1.0, readable-stream@~1.0.2: +readable-stream@1.0: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" dependencies: @@ -5028,6 +6070,26 @@ readable-stream@1.0, readable-stream@~1.0.2: isarray "0.0.1" string_decoder "~0.10.x" +readable-stream@1.1.x, "readable-stream@1.x >=1.1.9": + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~2.0.0, readable-stream@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + 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" + readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -5044,6 +6106,22 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redis-commands@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.1.tgz#81d826f45fa9c8b2011f4cd7a0fe597d241d442b" + +redis-parser@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" + +redis@^2.7.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" + dependencies: + double-ended-queue "^2.1.0-0" + redis-commands "^1.2.0" + redis-parser "^2.6.0" + reduce-css-calc@^1.2.6: version "1.3.0" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" @@ -5076,6 +6154,12 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" +regex-not@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.0.tgz#42f83e39771622df826b02af176525d6a5f157f9" + dependencies: + extend-shallow "^2.0.1" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -5120,7 +6204,7 @@ repeat-string@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -5130,7 +6214,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2, request@^2.72.0, request@^2.78.0, request@^2.79.0, request@^2.81.0: +request@2, request@^2.0.0, request@^2.72.0, request@^2.74.0, request@^2.78.0, request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -5157,6 +6241,66 @@ request@2, request@^2.72.0, request@^2.78.0, request@^2.79.0, request@^2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@2.75.x: + version "2.75.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + bl "~1.1.2" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.0.0" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.2.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + +request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +requestretry@^1.2.2: + version "1.13.0" + resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94" + dependencies: + extend "^3.0.0" + lodash "^4.15.0" + request "^2.74.0" + when "^3.7.7" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -5183,7 +6327,15 @@ resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2: +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.7, resolve@^1.3.2: version "1.4.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" dependencies: @@ -5226,23 +6378,17 @@ rxjs-websockets@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/rxjs-websockets/-/rxjs-websockets-4.0.0.tgz#a8d06c74b8629a9f9d56450eda5c3177542c80f4" -rxjs@^5.0.1: - version "5.4.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.3.tgz#0758cddee6033d68e0fd53676f0f3596ce3d483f" +rxjs@^5.5.6: + version "5.5.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.6.tgz#e31fb96d6fd2ff1fd84bcea8ae9c02d007179c02" dependencies: - symbol-observable "^1.0.1" - -rxjs@^5.5.2: - version "5.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3" - dependencies: - symbol-observable "^1.0.1" + symbol-observable "1.0.1" safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -sass-graph@^2.1.1: +sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" dependencies: @@ -5251,7 +6397,7 @@ sass-graph@^2.1.1: scss-tokenizer "^0.2.3" yargs "^7.0.0" -sass-loader@^6.0.3: +sass-loader@^6.0.6: version "6.0.6" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" dependencies: @@ -5285,6 +6431,13 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" +schema-utils@^0.4.0, schema-utils@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.3.tgz#e2a594d3395834d5e15da22b48be13517859458e" + dependencies: + ajv "^5.0.0" + ajv-keywords "^2.1.0" + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -5331,10 +6484,6 @@ semver-dsl@^1.0.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" -semver@~4.3.3: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" @@ -5343,24 +6492,28 @@ semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -send@0.15.4: - version "0.15.4" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9" +send@0.16.1: + version "0.16.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" dependencies: - debug "2.6.8" + debug "2.6.9" depd "~1.1.1" destroy "~1.0.4" encodeurl "~1.0.1" escape-html "~1.0.3" - etag "~1.8.0" - fresh "0.5.0" + etag "~1.8.1" + fresh "0.5.2" http-errors "~1.6.2" - mime "1.3.4" + mime "1.4.1" ms "2.0.0" on-finished "~2.3.0" range-parser "~1.2.0" statuses "~1.3.1" +serialize-javascript@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005" + serve-index@^1.7.2: version "1.9.0" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.0.tgz#d2b280fc560d616ee81b48bf0fa82abed2485ce7" @@ -5373,23 +6526,47 @@ serve-index@^1.7.2: mime-types "~2.1.15" parseurl "~1.3.1" -serve-static@1.12.4: - version "1.12.4" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961" +serve-static@1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.15.4" + parseurl "~1.3.2" + send "0.16.1" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" +set-getter@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" + dependencies: + to-object-path "^0.3.0" + set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -5398,7 +6575,11 @@ setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" -sha.js@^2.4.0, sha.js@^2.4.8: +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + +sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: version "2.4.8" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" dependencies: @@ -5413,6 +6594,13 @@ shallow-clone@^0.1.2: lazy-cache "^0.2.3" mixin-object "^2.0.1" +shasum@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" + dependencies: + json-stable-stringify "~0.0.0" + sha.js "~2.4.4" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -5423,6 +6611,15 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -5433,55 +6630,96 @@ silent-error@^1.0.0: dependencies: debug "^2.2.0" +slack-node@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/slack-node/-/slack-node-0.2.0.tgz#de4b8dddaa8b793f61dbd2938104fdabf37dfa30" + dependencies: + requestretry "^1.2.2" + +smart-buffer@^1.0.4: + version "1.1.15" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" + +smtp-connection@2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1" + dependencies: + httpntlm "1.6.1" + nodemailer-shared "1.1.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^2.0.0" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" dependencies: hoek "2.x.x" -socket.io-adapter@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b" - dependencies: - debug "2.3.3" - socket.io-parser "2.3.1" +socket.io-adapter@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b" -socket.io-client@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377" +socket.io-client@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.4.tgz#0918a552406dc5e540b380dcd97afc4a64332f8e" dependencies: backo2 "1.0.2" + base64-arraybuffer "0.1.5" component-bind "1.0.0" component-emitter "1.2.1" - debug "2.3.3" - engine.io-client "1.8.3" - has-binary "0.1.7" + debug "~2.6.4" + engine.io-client "~3.1.0" + has-cors "1.1.0" indexof "0.0.1" object-component "0.0.3" + parseqs "0.0.5" parseuri "0.0.5" - socket.io-parser "2.3.1" + socket.io-parser "~3.1.1" to-array "0.1.4" -socket.io-parser@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0" +socket.io-parser@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.2.tgz#dbc2282151fc4faebbe40aeedc0772eba619f7f2" dependencies: - component-emitter "1.1.2" - debug "2.2.0" - isarray "0.0.1" - json3 "3.3.2" + component-emitter "1.2.1" + debug "~2.6.4" + has-binary2 "~1.0.2" + isarray "2.0.1" -socket.io@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b" +socket.io@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.4.tgz#c1a4590ceff87ecf13c72652f046f716b29e6014" dependencies: - debug "2.3.3" - engine.io "1.8.3" - has-binary "0.1.7" - object-assign "4.1.0" - socket.io-adapter "0.5.0" - socket.io-client "1.7.3" - socket.io-parser "2.3.1" + debug "~2.6.6" + engine.io "~3.1.0" + socket.io-adapter "~1.1.0" + socket.io-client "2.0.4" + socket.io-parser "~3.1.1" sockjs-client@1.1.4: version "1.1.4" @@ -5494,12 +6732,27 @@ sockjs-client@1.1.4: json3 "^3.3.2" url-parse "^1.1.8" -sockjs@0.3.18: - version "0.3.18" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" dependencies: faye-websocket "^0.10.0" - uuid "^2.0.2" + uuid "^3.0.1" + +socks-proxy-agent@2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3" + dependencies: + agent-base "2" + extend "3" + socks "~1.1.5" + +socks@1.1.9, socks@~1.1.5: + version "1.1.9" + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691" + dependencies: + ip "^1.1.2" + smart-buffer "^1.0.4" sort-keys@^1.0.0: version "1.1.2" @@ -5515,27 +6768,39 @@ source-list-map@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" -source-map-loader@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.1.tgz#48126be9230bd47fad05e46a8c3c2e3d2dabe507" +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" dependencies: - async "^0.9.0" - loader-utils "~0.2.2" - source-map "~0.1.33" + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" -source-map-support@^0.4.0, source-map-support@^0.4.1, source-map-support@^0.4.2, source-map-support@~0.4.0: +source-map-support@^0.4.1, source-map-support@^0.4.2, source-map-support@~0.4.0: version "0.4.17" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.17.tgz#6f2150553e6375375d0ccb3180502b78c18ba430" dependencies: source-map "^0.5.6" -source-map@0.1.x, source-map@~0.1.33: +source-map-support@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76" + dependencies: + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@0.1.x: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: amdefine ">=0.0.4" -source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.3: +source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3, source-map@~0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -5545,7 +6810,7 @@ source-map@^0.4.2, source-map@^0.4.4, source-map@~0.4.1: dependencies: amdefine ">=0.0.4" -source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -5586,6 +6851,12 @@ spdy@^3.4.1: select-hose "^2.0.0" spdy-transport "^2.0.18" +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + sprintf-js@^1.0.3: version "1.1.1" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" @@ -5614,6 +6885,13 @@ ssri@^5.0.0: dependencies: safe-buffer "^5.1.0" +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + "statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -5624,13 +6902,20 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stream-browserify@^2.0.1: +stream-browserify@^2.0.0, stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" dependencies: inherits "~2.0.1" readable-stream "^2.0.2" +stream-combiner2@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" + dependencies: + duplexer2 "~0.1.0" + readable-stream "^2.0.2" + stream-each@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" @@ -5638,7 +6923,7 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" -stream-http@^2.3.1: +stream-http@^2.0.0, stream-http@^2.3.1: version "2.7.2" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" dependencies: @@ -5652,6 +6937,22 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" +stream-splicer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.2" + +streamroller@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" + dependencies: + date-format "^1.2.0" + debug "^3.1.0" + mkdirp "^0.5.1" + readable-stream "^2.3.0" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -5675,7 +6976,7 @@ string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string_decoder@~1.0.3: +string_decoder@~1.0.0, string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: @@ -5746,6 +7047,12 @@ stylus@^0.54.5: sax "0.5.x" source-map "0.1.x" +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + dependencies: + minimist "^1.1.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -5756,12 +7063,18 @@ supports-color@^3.1.2, supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0: +supports-color@^4.0.0, supports-color@^4.2.1: version "4.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" dependencies: has-flag "^2.0.0" +supports-color@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" + dependencies: + has-flag "^2.0.0" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -5774,9 +7087,15 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -symbol-observable@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + +syntax-error@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062bb96ca1" + dependencies: + acorn "^4.0.3" tapable@^0.2.7: version "0.2.8" @@ -5810,10 +7129,14 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -through@X.X.X: +"through@>=2.2.7 <3", through@X.X.X: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +thunkify@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + thunky@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e" @@ -5822,12 +7145,22 @@ time-stamp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" +timers-browserify@^1.0.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + dependencies: + process "~0.11.0" + timers-browserify@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6" dependencies: setimmediate "^1.0.4" +timespan@2.3.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929" + tmp@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" @@ -5838,11 +7171,11 @@ tmp@0.0.30: dependencies: os-tmpdir "~1.0.1" -tmp@0.0.31, tmp@0.0.x: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" +tmp@0.0.33, tmp@0.0.x: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" dependencies: - os-tmpdir "~1.0.1" + os-tmpdir "~1.0.2" to-array@0.1.4: version "0.1.4" @@ -5856,6 +7189,27 @@ to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" + dependencies: + define-property "^0.2.5" + extend-shallow "^2.0.1" + regex-not "^1.0.0" + toposort@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.3.tgz#f02cd8a74bd8be2fc0e98611c3bacb95a171869c" @@ -5878,31 +7232,39 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -ts-node@~3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-3.2.2.tgz#bbd28e38af4aaa3e96076c466e1b220197c1a3ce" +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + +ts-node@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-4.1.0.tgz#36d9529c7b90bb993306c408cd07f7743de20712" dependencies: arrify "^1.0.0" - chalk "^2.0.0" + chalk "^2.3.0" diff "^3.1.0" make-error "^1.1.1" minimist "^1.2.0" mkdirp "^0.5.1" - source-map-support "^0.4.0" - tsconfig "^6.0.0" + source-map-support "^0.5.0" + tsconfig "^7.0.0" v8flags "^3.0.0" yn "^2.0.0" -tsconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-6.0.0.tgz#6b0e8376003d7af1864f8df8f89dd0059ffcd032" +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tsickle@^0.24.0: - version "0.24.1" - resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.24.1.tgz#039343b205bf517a333b0703978892f80a7d848e" +tsickle@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.26.0.tgz#40b30a2dd6abcb33b182e37596674bd1cfe4039c" dependencies: minimist "^1.2.0" mkdirp "^0.5.1" @@ -5913,28 +7275,38 @@ tslib@^1.7.1: version "1.7.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" -tslint@~5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552" +tslib@^1.8.0, tslib@^1.8.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" + +tslint@~5.9.1: + version "5.9.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae" dependencies: babel-code-frame "^6.22.0" - colors "^1.1.2" - commander "^2.9.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" minimatch "^3.0.4" resolve "^1.3.2" semver "^5.3.0" - tslib "^1.7.1" - tsutils "^2.8.1" + tslib "^1.8.0" + tsutils "^2.12.1" + +tsscmp@~1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" -tsutils@^2.8.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.12.1.tgz#f4d95ce3391c8971e46e54c4cf0edb0a21dd5b24" +tsutils@^2.12.1: + version "2.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.21.0.tgz#43466a2283a0abce64e2209bc732ad72f8a04fab" dependencies: - tslib "^1.7.1" + tslib "^1.8.1" -tty-browserify@0.0.0: +tty-browserify@0.0.0, tty-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -5944,10 +7316,20 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + type-is@~1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" @@ -5955,23 +7337,23 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -typedarray@^0.0.6: +typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@~2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844" +typescript@~2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" -typescript@~2.6.1: +typescript@~2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" -uglify-es@^3.1.3: - version "3.1.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.1.9.tgz#6c82df628ac9eb7af9c61fd70c744a084abe6161" +uglify-es@^3.3.4: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" dependencies: - commander "~2.11.0" + commander "~2.13.0" source-map "~0.6.1" uglify-js@3.0.x: @@ -5994,18 +7376,6 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" -uglifyjs-webpack-plugin@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.0.0.tgz#1c58b5db1ed043e024aef66f8ade25e148206264" - dependencies: - cacache "^10.0.0" - find-cache-dir "^1.0.0" - schema-utils "^0.3.0" - source-map "^0.5.6" - uglify-es "^3.1.3" - webpack-sources "^1.0.1" - worker-farm "^1.4.1" - uglifyjs-webpack-plugin@^0.4.6: version "0.4.6" resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" @@ -6014,6 +7384,19 @@ uglifyjs-webpack-plugin@^0.4.6: uglify-js "^2.8.29" webpack-sources "^1.0.1" +uglifyjs-webpack-plugin@^1.1.5: + version "1.1.8" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.8.tgz#1302fb9471a7daf3d0a5174da6d65f0f415e75ad" + dependencies: + cacache "^10.0.1" + find-cache-dir "^1.0.0" + schema-utils "^0.4.2" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -6022,6 +7405,27 @@ ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + +umd@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" + +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -6056,10 +7460,21 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + upper-case@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + url-loader@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7" @@ -6082,13 +7497,21 @@ url-parse@^1.1.8: querystringify "~1.0.0" requires-port "1.0.x" -url@^0.11.0: +url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: punycode "1.3.2" querystring "0.2.0" +use@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8" + dependencies: + define-property "^0.2.5" + isobject "^3.0.0" + lazy-cache "^2.0.2" + user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" @@ -6104,7 +7527,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@0.10.3, util@^0.10.3: +util@0.10.3, util@^0.10.3, util@~0.10.1: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -6122,14 +7545,18 @@ utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uuid@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" -uuid@^3.0.0: +uuid@^3.0.0, uuid@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +uws@~0.14.4: + version "0.14.5" + resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc" + v8flags@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.0.tgz#4be9604488e0c4123645def705b1848d16b8e01f" @@ -6147,6 +7574,10 @@ vary@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" @@ -6163,7 +7594,7 @@ vlq@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1" -vm-browserify@0.0.4: +vm-browserify@0.0.4, vm-browserify@~0.0.1: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" dependencies: @@ -6214,13 +7645,6 @@ webdriver-manager@^12.0.6: semver "^5.3.0" xml2js "^0.4.17" -webpack-concat-plugin@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/webpack-concat-plugin/-/webpack-concat-plugin-1.4.0.tgz#a6eb3f0082d03c79d8ee2f1518c7f48e44ee12c5" - dependencies: - md5 "^2.2.1" - uglify-js "^2.8.29" - webpack-core@^0.6.8: version "0.6.9" resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2" @@ -6228,7 +7652,17 @@ webpack-core@^0.6.8: source-list-map "~0.1.7" source-map "~0.4.1" -webpack-dev-middleware@^1.11.0, webpack-dev-middleware@~1.12.0: +webpack-dev-middleware@1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e" + dependencies: + memory-fs "~0.4.1" + mime "^1.5.0" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + time-stamp "^2.0.0" + +webpack-dev-middleware@~1.12.0: version "1.12.0" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz#d34efefb2edda7e1d3b5dbe07289513219651709" dependencies: @@ -6238,22 +7672,22 @@ webpack-dev-middleware@^1.11.0, webpack-dev-middleware@~1.12.0: range-parser "^1.0.3" time-stamp "^2.0.0" -webpack-dev-server@~2.9.3: - version "2.9.4" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.9.4.tgz#7883e61759c6a4b33e9b19ec4037bd4ab61428d1" +webpack-dev-server@~2.11.0: + version "2.11.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz#6f9358a002db8403f016e336816f4485384e5ec0" dependencies: ansi-html "0.0.7" array-includes "^3.0.3" bonjour "^3.5.0" - chokidar "^1.6.0" + chokidar "^2.0.0" compression "^1.5.2" connect-history-api-fallback "^1.3.0" debug "^3.1.0" del "^3.0.0" - express "^4.13.3" + express "^4.16.2" html-entities "^1.2.0" http-proxy-middleware "~0.17.4" - import-local "^0.1.1" + import-local "^1.0.0" internal-ip "1.2.0" ip "^1.1.5" killable "^1.0.0" @@ -6262,13 +7696,13 @@ webpack-dev-server@~2.9.3: portfinder "^1.0.9" selfsigned "^1.9.1" serve-index "^1.7.2" - sockjs "0.3.18" + sockjs "0.3.19" sockjs-client "1.1.4" spdy "^3.4.1" - strip-ansi "^3.0.1" - supports-color "^4.2.1" - webpack-dev-middleware "^1.11.0" - yargs "^6.6.0" + strip-ansi "^3.0.0" + supports-color "^5.1.0" + webpack-dev-middleware "1.12.2" + yargs "6.6.0" webpack-merge@^4.1.0: version "4.1.0" @@ -6283,15 +7717,22 @@ webpack-sources@^1.0.0, webpack-sources@^1.0.1: source-list-map "^2.0.0" source-map "~0.5.3" +webpack-sources@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + webpack-subresource-integrity@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.0.1.tgz#1fc09d46497da66e46743a2a51d2cc385b9cb0ed" dependencies: webpack-core "^0.6.8" -webpack@~3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.8.1.tgz#b16968a81100abe61608b0153c9159ef8bb2bd83" +webpack@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" @@ -6326,6 +7767,10 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" +when@^3.7.7: + version "3.7.8" + resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" + when@~3.6.x: version "3.6.4" resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" @@ -6366,7 +7811,11 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" -worker-farm@^1.4.1: +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.2.tgz#32b312e5dc3d5d45d79ef44acc2587491cd729ae" dependencies: @@ -6384,16 +7833,20 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -ws@1.1.2, ws@^1.0.1: +ws@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" dependencies: options ">=0.0.5" ultron "1.0.x" -wtf-8@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a" +ws@~3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" xhr2@^0.1.4: version "0.1.4" @@ -6421,14 +7874,18 @@ xmlbuilder@>=1.0.0, xmlbuilder@~9.0.1: version "9.0.4" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" -xmlhttprequest-ssl@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" xmlhttprequest@1: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -6465,7 +7922,7 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" -yargs@^6.6.0: +yargs@6.6.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" dependencies: @@ -6536,6 +7993,6 @@ yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" -zone.js@^0.8.14: - version "0.8.17" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.17.tgz#4c5e5185a857da8da793daf3919371c5a36b2a0b" +zone.js@^0.8.19: + version "0.8.20" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.20.tgz#a218c48db09464b19ff6fc8f0d4bb5b1046e185d" From 0a942f4f3ea95a9813e5476c61355c4ecf3ea3fb Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Feb 2018 20:45:42 +0100 Subject: [PATCH 028/156] Rename to UiWebsocket + EdgeWebsocket for consistency. Start re-implementation of UiWebsocket --- .../org.eclipse.core.resources.prefs | 7 +- .../org.eclipse.core.resources.prefs | 2 - .../BackendApp.bndrun | 6 +- io.openems.backend.application/bnd.bnd | 4 +- .../backend/application/BackendApp.java | 8 +- .../org.eclipse.core.resources.prefs | 6 - .../api/BrowserWebsocketService.java | 8 - .../org.eclipse.core.resources.prefs | 7 - .../bnd.bnd | 20 -- ...serwebsocket.impl.provider.bndrun.resolved | 12 ++ .../impl/provider/package-info.java | 2 - io.openems.backend.common/bnd.bnd | 2 +- .../common/session/SessionManager.java | 5 + .../{types => session}/package-info.java | 2 +- .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 6 +- .../readme.md | 0 .../api/EdgeWebsocketService.java | 7 + .../edgewebsocket}/api/package-info.java | 2 +- .../test/.gitignore | 0 .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../org.eclipse.core.resources.prefs | 9 + .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 8 +- .../debug.bndrun | 13 ++ ...nems.backend.edgewebsocket.provider.bndrun | 8 +- .../readme.md | 0 .../impl/provider/EdgeWebsocket.java | 20 +- .../impl/provider/package-info.java | 2 + .../provider/ProviderImplTest.java | 6 +- .../edgewebsocket/provider/package-info.java | 2 + .../backend/metadata/api/MetadataService.java | 2 +- ...ataDevice.java => OLD_MetadataDevice.java} | 2 +- ...odel.java => OLD_MetadataDeviceModel.java} | 4 +- ...aDevices.java => OLD_MetadataDevices.java} | 12 +- .../backend/metadata/api/OpenemsEdge.java | 13 ++ .../io/openems/backend/metadata/api/User.java | 18 ++ .../openems/backend/metadata/odoo/Odoo.java | 6 +- .../org.eclipse.core.resources.prefs | 6 - .../api/OpenemsWebsocketService.java | 7 - .../org.eclipse.core.resources.prefs | 8 - .../debug.bndrun | 13 -- .../impl/provider/OpenemsWebsocket.java | 35 ---- .../impl/provider/package-info.java | 2 - ....openemswebsocket.provider.bndrun.resolved | 2 +- .../backend/timedata/api/TimedataService.java | 4 +- .../backend/timedata/influx/Influx.java | 12 +- .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 3 +- .../readme.md | 0 .../uiwebsocket/api/UiWebsocketService.java | 8 + .../uiwebsocket}/api/package-info.java | 2 +- .../test/.gitignore | 0 .../.classpath | 0 .../.gitignore | 0 .../.project | 2 +- .../org.eclipse.core.resources.prefs | 7 + .../.settings/org.eclipse.jdt.core.prefs | 0 .../bnd.bnd | 26 +++ .../debug.bndrun | 0 ...kend.browserwebsocket.impl.provider.bndrun | 11 +- .../readme.md | 0 .../provider/AbstractWebsocketServer.java | 188 ++++++++++++++++++ .../impl/provider/UiWebsocket.java | 76 +++++++ .../impl/provider/UiWebsocketServer.java | 94 +++++++++ .../impl/provider/package-info.java | 2 + .../impl/provider/ProviderImplTest.java | 6 +- .../jar/Java-WebSocket-1.3.6.jar | Bin 108198 -> 0 bytes .../jar/Java-WebSocket-1.3.7.jar | Bin 0 -> 111418 bytes io.openems.wrapper/websocket.bnd | 6 +- ui/src/environments/environment.ts | 2 +- 81 files changed, 576 insertions(+), 195 deletions(-) delete mode 100644 io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java delete mode 100644 io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.browserwebsocket.impl.provider/bnd.bnd create mode 100644 io.openems.backend.browserwebsocket.impl.provider/generated/io.openems.backend.browserwebsocket.impl.provider.bndrun.resolved delete mode 100644 io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java create mode 100644 io.openems.backend.common/src/io/openems/backend/common/session/SessionManager.java rename io.openems.backend.common/src/io/openems/backend/common/{types => session}/package-info.java (53%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.edgewebsocket.api}/.classpath (100%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.edgewebsocket.api}/.gitignore (100%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.edgewebsocket.api}/.project (90%) create mode 100644 io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs rename {io.openems.backend.browserwebsocket.api => io.openems.backend.edgewebsocket.api}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.openemswebsocket.api => io.openems.backend.edgewebsocket.api}/bnd.bnd (62%) rename {io.openems.backend.openemswebsocket.api => io.openems.backend.edgewebsocket.api}/readme.md (100%) create mode 100644 io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java rename {io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket => io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket}/api/package-info.java (50%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.edgewebsocket.api}/test/.gitignore (100%) rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.edgewebsocket.impl.provider}/.classpath (100%) rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.edgewebsocket.impl.provider}/.gitignore (100%) rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.edgewebsocket.impl.provider}/.project (88%) create mode 100644 io.openems.backend.edgewebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.edgewebsocket.impl.provider}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.openemswebsocket.impl.provider => io.openems.backend.edgewebsocket.impl.provider}/bnd.bnd (54%) create mode 100644 io.openems.backend.edgewebsocket.impl.provider/debug.bndrun rename io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun => io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun (64%) rename {io.openems.backend.openemswebsocket.impl.provider => io.openems.backend.edgewebsocket.impl.provider}/readme.md (100%) rename io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java => io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java (50%) create mode 100644 io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/package-info.java rename {io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket => io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket}/provider/ProviderImplTest.java (57%) create mode 100644 io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/package-info.java rename io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/{MetadataDevice.java => OLD_MetadataDevice.java} (95%) rename io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/{MetadataDeviceModel.java => OLD_MetadataDeviceModel.java} (63%) rename io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/{MetadataDevices.java => OLD_MetadataDevices.java} (66%) create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OpenemsEdge.java create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java delete mode 100644 io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java delete mode 100644 io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs delete mode 100644 io.openems.backend.openemswebsocket.impl.provider/debug.bndrun delete mode 100644 io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java delete mode 100644 io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java rename {io.openems.backend.openemswebsocket.api => io.openems.backend.uiwebsocket.api}/.classpath (100%) rename {io.openems.backend.openemswebsocket.api => io.openems.backend.uiwebsocket.api}/.gitignore (100%) rename {io.openems.backend.openemswebsocket.api => io.openems.backend.uiwebsocket.api}/.project (90%) create mode 100644 io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs rename {io.openems.backend.openemswebsocket.api => io.openems.backend.uiwebsocket.api}/.settings/org.eclipse.jdt.core.prefs (100%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.uiwebsocket.api}/bnd.bnd (83%) rename {io.openems.backend.browserwebsocket.api => io.openems.backend.uiwebsocket.api}/readme.md (100%) create mode 100644 io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java rename {io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket => io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket}/api/package-info.java (50%) rename {io.openems.backend.openemswebsocket.api => io.openems.backend.uiwebsocket.api}/test/.gitignore (100%) rename {io.openems.backend.openemswebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/.classpath (100%) rename {io.openems.backend.openemswebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/.gitignore (100%) rename {io.openems.backend.openemswebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/.project (88%) create mode 100644 io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs rename {io.openems.backend.openemswebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/.settings/org.eclipse.jdt.core.prefs (100%) create mode 100644 io.openems.backend.uiwebsocket.impl.provider/bnd.bnd rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/debug.bndrun (100%) rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/io.openems.backend.browserwebsocket.impl.provider.bndrun (60%) rename {io.openems.backend.browserwebsocket.impl.provider => io.openems.backend.uiwebsocket.impl.provider}/readme.md (100%) create mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java create mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java create mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java create mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/package-info.java rename {io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket => io.openems.backend.uiwebsocket.impl.provider/test/io/openems/backend/uiwebsocket}/impl/provider/ProviderImplTest.java (56%) delete mode 100644 io.openems.wrapper/jar/Java-WebSocket-1.3.6.jar create mode 100644 io.openems.wrapper/jar/Java-WebSocket-1.3.7.jar diff --git a/common/.settings/org.eclipse.core.resources.prefs b/common/.settings/org.eclipse.core.resources.prefs index 6d3e8f00f7c..7a531392842 100644 --- a/common/.settings/org.eclipse.core.resources.prefs +++ b/common/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,3 @@ -eclipse.preferences.version=1 -encoding//src/test/java=UTF-8 -encoding/=UTF-8 -encoding/src=UTF-8 +eclipse.preferences.version=1 +encoding/=UTF-8 +encoding/src=UTF-8 diff --git a/edge/.settings/org.eclipse.core.resources.prefs b/edge/.settings/org.eclipse.core.resources.prefs index 2e41dd01076..22da5786879 100644 --- a/edge/.settings/org.eclipse.core.resources.prefs +++ b/edge/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,4 @@ eclipse.preferences.version=1 encoding//src/io/openems/impl/scheduler/SimpleScheduler.java=UTF-8 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 34ee53c4c06..3d07a17c077 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -23,8 +23,10 @@ JPM-Command: openems-backend com.google.gson;version='[2.8.2,2.8.3)',\ com.google.guava;version='[19.0.0,19.0.1)',\ io.openems.backend.application;version=snapshot,\ + io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ io.openems.backend.metadata.odoo.provider;version=snapshot,\ io.openems.backend.timedata.influx.provider;version=snapshot,\ + io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ io.openems.common;version=snapshot,\ io.openems.wrapper.websocket;version=snapshot,\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ @@ -37,6 +39,4 @@ JPM-Command: openems-backend org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - io.openems.backend.openemswebsocket.impl.provider;version=snapshot,\ - io.openems.backend.browserwebsocket.impl.provider;version=snapshot \ No newline at end of file + org.osgi.service.metatype;version='[1.3.0,1.3.1)' \ No newline at end of file diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index 0fd562cfd88..079913a0d0a 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -15,8 +15,8 @@ Private-Package: \ io.openems.backend.metadata.api;version=latest,\ io.openems.common;version=latest,\ io.openems.backend.timedata.api;version=latest,\ - io.openems.backend.openemswebsocket.api;version=latest,\ - io.openems.backend.browserwebsocket.api;version=latest + io.openems.backend.edgewebsocket.api;version=latest,\ + io.openems.backend.uiwebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 34c1d0f1f90..54e9daccb4d 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -12,10 +12,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; +import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; import io.openems.backend.timedata.api.TimedataService; +import io.openems.backend.uiwebsocket.api.UiWebsocketService; @Component() public class BackendApp { @@ -32,10 +32,10 @@ public class BackendApp { TimedataService timedataService; @Reference - OpenemsWebsocketService openemsWebsocketService; + EdgeWebsocketService edgeWebsocketService; @Reference - BrowserWebsocketService browserWebsocketService; + UiWebsocketService uiWebsocketService; @Activate void activate() { diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 26e809422ae..00000000000 --- a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java=UTF-8 -encoding//src/io/openems/backend/browserwebsocket/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java b/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java deleted file mode 100644 index a7bf79d079c..00000000000 --- a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/BrowserWebsocketService.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.openems.backend.browserwebsocket.api; - -import org.osgi.annotation.versioning.ProviderType; - -@ProviderType -public interface BrowserWebsocketService { - -} diff --git a/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index d164fdd2828..00000000000 --- a/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java=UTF-8 -encoding//test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/debug.bndrun=UTF-8 -encoding/io.openems.backend.browserwebsocket.impl.provider.bndrun=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.impl.provider/bnd.bnd b/io.openems.backend.browserwebsocket.impl.provider/bnd.bnd deleted file mode 100644 index 7f014c574b3..00000000000 --- a/io.openems.backend.browserwebsocket.impl.provider/bnd.bnd +++ /dev/null @@ -1,20 +0,0 @@ -# -# io.openems.backend.browserwebsocket.impl.provider PROVIDER BUNDLE -# - -Bundle-Version: 1.0.0.${tstamp} - -Export-Package: io.openems.backend.browserwebsocket.api - -Private-Package: io.openems.backend.browserwebsocket.impl.provider - --includeresource: {readme.md} - --buildpath: \ - osgi.enroute.base.api;version=2.1,\ - io.openems.backend.browserwebsocket.api;version=latest - --testpath: \ - osgi.enroute.junit.wrapper;version=4.12, \ - osgi.enroute.hamcrest.wrapper;version=1.3 - diff --git a/io.openems.backend.browserwebsocket.impl.provider/generated/io.openems.backend.browserwebsocket.impl.provider.bndrun.resolved b/io.openems.backend.browserwebsocket.impl.provider/generated/io.openems.backend.browserwebsocket.impl.provider.bndrun.resolved new file mode 100644 index 00000000000..e2613309b7c --- /dev/null +++ b/io.openems.backend.browserwebsocket.impl.provider/generated/io.openems.backend.browserwebsocket.impl.provider.bndrun.resolved @@ -0,0 +1,12 @@ +slf4j.api;version='[1.8.0,1.8.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/slf4j/slf4j-api/1.8.0-beta1/slf4j-api-1.8.0-beta1.jar +com.google.guava;version='[19.0.0,19.0.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/com/google/guava/guava/19.0/guava-19.0.jar +org.apache.felix.configadmin;version='[1.8.8,1.8.9)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.configadmin/1.8.8/org.apache.felix.configadmin-1.8.8.jar +org.osgi.service.metatype;version='[1.3.0,1.3.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/osgi/org.osgi.service.metatype/1.3.0/org.osgi.service.metatype-1.3.0.jar +io.openems.common;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.common/generated/io.openems.common.jar +org.apache.felix.log;version='[1.0.1,1.0.2)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.log/1.0.1/org.apache.felix.log-1.0.1.jar +com.google.gson;version='[2.8.2,2.8.3)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/com/google/code/gson/gson/2.8.2/gson-2.8.2.jar +org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/osgi/org.eclipse.equinox.metatype/1.4.100.v20150408-1437/org.eclipse.equinox.metatype-1.4.100.v20150408-1437.jar +org.apache.felix.scr;version='[2.0.2,2.0.3)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.scr/2.0.2/org.apache.felix.scr-2.0.2.jar +io.openems.wrapper.websocket;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.wrapper/generated/io.openems.wrapper.websocket.jar +io.openems.backend.metadata.odoo.provider;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.backend.metadata.odoo.provider/generated/io.openems.backend.metadata.odoo.provider.jar +io.openems.backend.uiwebsocket.impl.provider;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.backend.uiwebsocket.impl.provider/generated/io.openems.backend.uiwebsocket.impl.provider.jar diff --git a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java b/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java deleted file mode 100644 index b3435d7102d..00000000000 --- a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.browserwebsocket.impl.provider; diff --git a/io.openems.backend.common/bnd.bnd b/io.openems.backend.common/bnd.bnd index 956109105b1..5c2603bddaf 100644 --- a/io.openems.backend.common/bnd.bnd +++ b/io.openems.backend.common/bnd.bnd @@ -4,7 +4,7 @@ Bundle-Version: 1.0.0.${tstamp} -Export-Package: io.openems.backend.common.types +Export-Package: io.openems.backend.common.session -includeresource: {readme.md} diff --git a/io.openems.backend.common/src/io/openems/backend/common/session/SessionManager.java b/io.openems.backend.common/src/io/openems/backend/common/session/SessionManager.java new file mode 100644 index 00000000000..26255e20aa0 --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/session/SessionManager.java @@ -0,0 +1,5 @@ +package io.openems.backend.common.session; + +public class SessionManager { + +} diff --git a/io.openems.backend.common/src/io/openems/backend/common/types/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/session/package-info.java similarity index 53% rename from io.openems.backend.common/src/io/openems/backend/common/types/package-info.java rename to io.openems.backend.common/src/io/openems/backend/common/session/package-info.java index f6e1f853daa..664f7b8026e 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/types/package-info.java +++ b/io.openems.backend.common/src/io/openems/backend/common/session/package-info.java @@ -1,2 +1,2 @@ @org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.common.types; +package io.openems.backend.common.session; diff --git a/io.openems.backend.browserwebsocket.api/.classpath b/io.openems.backend.edgewebsocket.api/.classpath similarity index 100% rename from io.openems.backend.browserwebsocket.api/.classpath rename to io.openems.backend.edgewebsocket.api/.classpath diff --git a/io.openems.backend.browserwebsocket.api/.gitignore b/io.openems.backend.edgewebsocket.api/.gitignore similarity index 100% rename from io.openems.backend.browserwebsocket.api/.gitignore rename to io.openems.backend.edgewebsocket.api/.gitignore diff --git a/io.openems.backend.browserwebsocket.api/.project b/io.openems.backend.edgewebsocket.api/.project similarity index 90% rename from io.openems.backend.browserwebsocket.api/.project rename to io.openems.backend.edgewebsocket.api/.project index 6a7fcd32642..48f802565d5 100644 --- a/io.openems.backend.browserwebsocket.api/.project +++ b/io.openems.backend.edgewebsocket.api/.project @@ -1,6 +1,6 @@ - io.openems.backend.browserwebsocket.api + io.openems.backend.edgewebsocket.api diff --git a/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..eaaee926cb7 --- /dev/null +++ b/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java=UTF-8 +encoding//src/io/openems/backend/edgewebsocket/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.browserwebsocket.api/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.edgewebsocket.api/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.openemswebsocket.api/bnd.bnd b/io.openems.backend.edgewebsocket.api/bnd.bnd similarity index 62% rename from io.openems.backend.openemswebsocket.api/bnd.bnd rename to io.openems.backend.edgewebsocket.api/bnd.bnd index bdd3a655518..b74585e15b8 100644 --- a/io.openems.backend.openemswebsocket.api/bnd.bnd +++ b/io.openems.backend.edgewebsocket.api/bnd.bnd @@ -1,11 +1,10 @@ # -# io.openems.backend.openemswebsocket.api DEFAULTS +# io.openems.backend.edgewebsocket.api DEFAULTS # Bundle-Version: 1.0.0.${tstamp} -Export-Package: \ - io.openems.backend.openemswebsocket.api +Export-Package: io.openems.backend.edgewebsocket.api Require-Capability: \ compile-only @@ -19,3 +18,4 @@ Require-Capability: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.api/readme.md b/io.openems.backend.edgewebsocket.api/readme.md similarity index 100% rename from io.openems.backend.openemswebsocket.api/readme.md rename to io.openems.backend.edgewebsocket.api/readme.md diff --git a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java new file mode 100644 index 00000000000..d1c683192a5 --- /dev/null +++ b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java @@ -0,0 +1,7 @@ +package io.openems.backend.edgewebsocket.api; + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface EdgeWebsocketService { +} diff --git a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/package-info.java similarity index 50% rename from io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java rename to io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/package-info.java index b1bf5e6e5e2..e7707f57e82 100644 --- a/io.openems.backend.browserwebsocket.api/src/io/openems/backend/browserwebsocket/api/package-info.java +++ b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/package-info.java @@ -1,2 +1,2 @@ @org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.browserwebsocket.api; +package io.openems.backend.edgewebsocket.api; diff --git a/io.openems.backend.browserwebsocket.api/test/.gitignore b/io.openems.backend.edgewebsocket.api/test/.gitignore similarity index 100% rename from io.openems.backend.browserwebsocket.api/test/.gitignore rename to io.openems.backend.edgewebsocket.api/test/.gitignore diff --git a/io.openems.backend.browserwebsocket.impl.provider/.classpath b/io.openems.backend.edgewebsocket.impl.provider/.classpath similarity index 100% rename from io.openems.backend.browserwebsocket.impl.provider/.classpath rename to io.openems.backend.edgewebsocket.impl.provider/.classpath diff --git a/io.openems.backend.browserwebsocket.impl.provider/.gitignore b/io.openems.backend.edgewebsocket.impl.provider/.gitignore similarity index 100% rename from io.openems.backend.browserwebsocket.impl.provider/.gitignore rename to io.openems.backend.edgewebsocket.impl.provider/.gitignore diff --git a/io.openems.backend.browserwebsocket.impl.provider/.project b/io.openems.backend.edgewebsocket.impl.provider/.project similarity index 88% rename from io.openems.backend.browserwebsocket.impl.provider/.project rename to io.openems.backend.edgewebsocket.impl.provider/.project index 559cee152d1..80b498441e3 100644 --- a/io.openems.backend.browserwebsocket.impl.provider/.project +++ b/io.openems.backend.edgewebsocket.impl.provider/.project @@ -1,6 +1,6 @@ - io.openems.backend.browserwebsocket.impl.provider + io.openems.backend.edgewebsocket.impl.provider diff --git a/io.openems.backend.edgewebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.edgewebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..79c800857e6 --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java=UTF-8 +encoding//src/io/openems/backend/edgewebsocket/impl/provider/package-info.java=UTF-8 +encoding//test/io/openems/backend/edgewebsocket/provider/ProviderImplTest.java=UTF-8 +encoding//test/io/openems/backend/edgewebsocket/provider/package-info.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.edgewebsocket.provider.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.edgewebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.browserwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.edgewebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.openemswebsocket.impl.provider/bnd.bnd b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd similarity index 54% rename from io.openems.backend.openemswebsocket.impl.provider/bnd.bnd rename to io.openems.backend.edgewebsocket.impl.provider/bnd.bnd index 7945141e0e6..66075229ef1 100644 --- a/io.openems.backend.openemswebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd @@ -1,21 +1,21 @@ # -# io.openems.backend.openemswebsocket.provider PROVIDER BUNDLE +# io.openems.backend.edgewebsocket.provider PROVIDER BUNDLE # Bundle-Version: 1.0.0.${tstamp} -Export-Package: io.openems.backend.openemswebsocket.api +Export-Package: io.openems.backend.edgewebsocket.api -includeresource: {readme.md} -buildpath: \ osgi.enroute.base.api;version=2.1,\ - io.openems.backend.openemswebsocket.api;version=latest + io.openems.backend.edgewebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 -runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' -Private-Package: io.openems.backend.openemswebsocket.impl.provider \ No newline at end of file +Private-Package: io.openems.backend.edgewebsocket.impl.provider \ No newline at end of file diff --git a/io.openems.backend.edgewebsocket.impl.provider/debug.bndrun b/io.openems.backend.edgewebsocket.impl.provider/debug.bndrun new file mode 100644 index 00000000000..153b3b100fe --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.edgewebsocket.provider DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.edgewebsocket.provider.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun b/io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun similarity index 64% rename from io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun rename to io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun index f5ae5e60843..9a7da11fd64 100644 --- a/io.openems.backend.openemswebsocket.impl.provider/io.openems.backend.openemswebsocket.provider.bndrun +++ b/io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun @@ -1,20 +1,20 @@ # -# io.openems.backend.openemswebsocket.provider LAUNCH SPECIFICATION +# io.openems.backend.edgewebsocket.provider LAUNCH SPECIFICATION # Bundle-Version: 1.0.0.${tstamp} -Bundle-SymbolicName: io.openems.backend.openemswebsocket.provider.launch +Bundle-SymbolicName: io.openems.backend.edgewebsocket.provider.launch JPM-Command: provider -runbundles: \ org.apache.felix.log;version='[1.0.1,1.0.2)',\ - io.openems.backend.openemswebsocket.impl.provider;version=snapshot,\ + io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ slf4j.api;version='[1.8.0,1.8.1)' --runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.openemswebsocket.impl.provider)' \ No newline at end of file +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.edgewebsocket.impl.provider)' \ No newline at end of file diff --git a/io.openems.backend.openemswebsocket.impl.provider/readme.md b/io.openems.backend.edgewebsocket.impl.provider/readme.md similarity index 100% rename from io.openems.backend.openemswebsocket.impl.provider/readme.md rename to io.openems.backend.edgewebsocket.impl.provider/readme.md diff --git a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java similarity index 50% rename from io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java rename to io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 44bceed8209..49d54236078 100644 --- a/io.openems.backend.browserwebsocket.impl.provider/src/io/openems/backend/browserwebsocket/impl/provider/BrowserWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -1,35 +1,35 @@ -package io.openems.backend.browserwebsocket.impl.provider; +package io.openems.backend.edgewebsocket.impl.provider; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.browserwebsocket.api.BrowserWebsocketService; +import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +@Designate(ocd = EdgeWebsocket.Config.class, factory = false) +@Component(name = "EdgeWebsocket") +public class EdgeWebsocket implements EdgeWebsocketService { -@Designate( ocd=BrowserWebsocket.Config.class, factory=true) -@Component(name="BrowserWebsocket") -public class BrowserWebsocket implements BrowserWebsocketService { + private final Logger log = LoggerFactory.getLogger(EdgeWebsocket.class); - private final Logger log = LoggerFactory.getLogger(BrowserWebsocket.class); - @ObjectClassDefinition @interface Config { int port(); } + @Activate void activate(Config config) { - log.debug("Activate BrowserWebsocket [port=" + config.port() + "]"); + log.debug("Activate EdgeWebsocket [port=" + config.port() + "]"); } @Deactivate void deactivate() { - log.debug("Deactivate BrowserWebsocket"); + log.debug("Deactivate EdgeWebsocket"); } } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/package-info.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/package-info.java new file mode 100644 index 00000000000..1eb73947096 --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.edgewebsocket.impl.provider; diff --git a/io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java b/io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/ProviderImplTest.java similarity index 57% rename from io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java rename to io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/ProviderImplTest.java index 3b0909af43b..b6356b50b04 100644 --- a/io.openems.backend.openemswebsocket.impl.provider/test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java +++ b/io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/ProviderImplTest.java @@ -1,10 +1,10 @@ -package io.openems.backend.openemswebsocket.provider; +package io.openems.backend.edgewebsocket.provider; import static org.junit.Assert.assertNotNull; import org.junit.Test; -import io.openems.backend.openemswebsocket.impl.provider.OpenemsWebsocket; +import io.openems.backend.edgewebsocket.impl.provider.EdgeWebsocket; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - OpenemsWebsocket impl = new OpenemsWebsocket(); + EdgeWebsocket impl = new EdgeWebsocket(); assertNotNull(impl); } diff --git a/io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/package-info.java b/io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/package-info.java new file mode 100644 index 00000000000..25c9ac7883d --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/test/io/openems/backend/edgewebsocket/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.edgewebsocket.provider; diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 69a953d2cc1..c7a092326e0 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -9,6 +9,6 @@ public interface MetadataService { public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsException; - public MetadataDeviceModel getDeviceModel(); + public OLD_MetadataDeviceModel getDeviceModel(); } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java similarity index 95% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java index 014c00db115..d37364c24a1 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevice.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java @@ -7,7 +7,7 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.Device; -public interface MetadataDevice extends Device { +public interface OLD_MetadataDevice extends Device { @Override public default Optional getIdOpt() { diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDeviceModel.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java similarity index 63% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDeviceModel.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java index f013dc17e21..029b4770862 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDeviceModel.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java @@ -2,7 +2,7 @@ import io.openems.common.exceptions.OpenemsException; -public interface MetadataDeviceModel { +public interface OLD_MetadataDeviceModel { /** * Gets the devices for this apikey. * @@ -10,5 +10,5 @@ public interface MetadataDeviceModel { * @return device or null * @throws OpenemsException */ - public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException; + public OLD_MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException; } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevices.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java similarity index 66% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevices.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java index d6f2fa5e371..2a49d29fd9f 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataDevices.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java @@ -8,12 +8,12 @@ import com.google.gson.JsonArray; -public class MetadataDevices implements Iterable { - private List devices = new ArrayList<>(); +public class OLD_MetadataDevices implements Iterable { + private List devices = new ArrayList<>(); public Set getNames() { Set names = new HashSet<>(); - for (MetadataDevice device : this.devices) { + for (OLD_MetadataDevice device : this.devices) { names.add(device.getName()); } return names; @@ -27,18 +27,18 @@ public boolean isEmpty() { return this.devices.isEmpty(); } - public void add(MetadataDevice device) { + public void add(OLD_MetadataDevice device) { this.devices.add(device); } @Override - public Iterator iterator() { + public Iterator iterator() { return this.devices.iterator(); } public JsonArray toJson() { JsonArray j = new JsonArray(); - for (MetadataDevice device : this.devices) { + for (OLD_MetadataDevice device : this.devices) { j.add(device.toJsonObject()); } return j; diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OpenemsEdge.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OpenemsEdge.java new file mode 100644 index 00000000000..35aeda6af7b --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OpenemsEdge.java @@ -0,0 +1,13 @@ +package io.openems.backend.metadata.api; + +public class OpenemsEdge { + private final int id; + + public OpenemsEdge(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java new file mode 100644 index 00000000000..2dc2b93ded7 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java @@ -0,0 +1,18 @@ +package io.openems.backend.metadata.api; + +public class User { + private final int id; + private String name; + + public User(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public int getId() { + return id; + } +} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index d1e5d627f44..df7fa53d4cf 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -9,7 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.metadata.api.MetadataDeviceModel; +import io.openems.backend.metadata.api.OLD_MetadataDeviceModel; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.metadata.api.UserDevicesInfo; import io.openems.common.exceptions.OpenemsException; @@ -68,10 +68,10 @@ void deactivate() { log.debug("Deactivate Odoo"); } - private MetadataDeviceModel deviceModel; + private OLD_MetadataDeviceModel deviceModel; @Override - public MetadataDeviceModel getDeviceModel() { + public OLD_MetadataDeviceModel getDeviceModel() { return deviceModel; } diff --git a/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index e3e3c8999fe..00000000000 --- a/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java=UTF-8 -encoding//src/io/openems/backend/openemswebsocket/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java b/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java deleted file mode 100644 index 5665ded938e..00000000000 --- a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/OpenemsWebsocketService.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.openems.backend.openemswebsocket.api; - -import org.osgi.annotation.versioning.ProviderType; - -@ProviderType -public interface OpenemsWebsocketService { -} diff --git a/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index e0ad58c308d..00000000000 --- a/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,8 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java=UTF-8 -encoding//src/io/openems/backend/openemswebsocket/impl/provider/package-info.java=UTF-8 -encoding//test/io/openems/backend/openemswebsocket/provider/ProviderImplTest.java=UTF-8 -encoding/bnd.bnd=UTF-8 -encoding/debug.bndrun=UTF-8 -encoding/io.openems.backend.openemswebsocket.provider.bndrun=UTF-8 -encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.impl.provider/debug.bndrun b/io.openems.backend.openemswebsocket.impl.provider/debug.bndrun deleted file mode 100644 index fc23c8daa3e..00000000000 --- a/io.openems.backend.openemswebsocket.impl.provider/debug.bndrun +++ /dev/null @@ -1,13 +0,0 @@ -# -# io.openems.backend.openemswebsocket.provider DEBUG LAUNCH SPECFICATION -# - --include: ~io.openems.backend.openemswebsocket.provider.bndrun - --runrequires.debug: \ - ${debug-bundles} - --runtrace: true - --runbundles: \ - ${error;Resolve first} diff --git a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java deleted file mode 100644 index 6cc47118eb6..00000000000 --- a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/OpenemsWebsocket.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.openems.backend.openemswebsocket.impl.provider; - -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.backend.openemswebsocket.api.OpenemsWebsocketService; - -import org.osgi.service.metatype.annotations.Designate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; - -@Designate(ocd = OpenemsWebsocket.Config.class, factory = false) -@Component(name = "OpenemsWebsocket") -public class OpenemsWebsocket implements OpenemsWebsocketService { - - private final Logger log = LoggerFactory.getLogger(OpenemsWebsocket.class); - - @ObjectClassDefinition - @interface Config { - int port(); - } - - @Activate - void activate(Config config) { - log.debug("Activate OpenemsWebsocket [port=" + config.port() + "]"); - } - - @Deactivate - void deactivate() { - log.debug("Deactivate OpenemsWebsocket"); - } - -} diff --git a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java b/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java deleted file mode 100644 index 56ed60ff9d0..00000000000 --- a/io.openems.backend.openemswebsocket.impl.provider/src/io/openems/backend/openemswebsocket/impl/provider/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.openemswebsocket.impl.provider; diff --git a/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved b/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved index d214db2741a..97ed637b697 100644 --- a/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved +++ b/io.openems.backend.openemswebsocket.provider/generated/io.openems.backend.openemswebsocket.provider.bndrun.resolved @@ -1,4 +1,4 @@ -io.openems.backend.openemswebsocket.impl.provider;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.backend.openemswebsocket.impl.provider/generated/io.openems.backend.openemswebsocket.impl.provider.jar +io.openems.backend.edgewebsocket.impl.provider;version=snapshot;resolution=file:/C:/Users/stefan.feilmeier/fems/openems/io.openems.backend.edgewebsocket.impl.provider/generated/io.openems.backend.edgewebsocket.impl.provider.jar org.osgi.service.metatype;version='[1.3.0,1.3.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/osgi/org.osgi.service.metatype/1.3.0/org.osgi.service.metatype-1.3.0.jar slf4j.api;version='[1.8.0,1.8.1)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/slf4j/slf4j-api/1.8.0-beta1/slf4j-api-1.8.0-beta1.jar org.apache.felix.log;version='[1.0.1,1.0.2)';resolution=file:/C:/Users/stefan.feilmeier/.m2/repository/org/apache/felix/org.apache.felix.log/1.0.1/org.apache.felix.log-1.0.1.jar diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index 3a8aeb4c757..a84fe8014f4 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -8,7 +8,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.MetadataDevices; +import io.openems.backend.metadata.api.OLD_MetadataDevices; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @@ -30,7 +30,7 @@ public interface TimedataService { * } * */ - public void write(MetadataDevices devices, JsonObject jData); + public void write(OLD_MetadataDevices devices, JsonObject jData); public Optional getChannelValue(int deviceId, ChannelAddress channelAddress); diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index fccb189543e..44ce7d50eb0 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -26,8 +26,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.MetadataDevice; -import io.openems.backend.metadata.api.MetadataDevices; +import io.openems.backend.metadata.api.OLD_MetadataDevice; +import io.openems.backend.metadata.api.OLD_MetadataDevices; import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @@ -112,9 +112,9 @@ private void connect() throws Exception { * { "channel1": value, "channel2": value } } */ @Override - public void write(MetadataDevices devices, JsonObject jData) { + public void write(OLD_MetadataDevices devices, JsonObject jData) { TreeBasedTable data = TreeBasedTable.create(); - for (MetadataDevice device : devices) { + for (OLD_MetadataDevice device : devices) { int deviceId = device.getIdOpt().orElse(0); // get existing or create new DeviceCache @@ -211,7 +211,7 @@ public void write(MetadataDevices devices, JsonObject jData) { // TODO remove after full migration for ( - MetadataDevice device : devices) { + OLD_MetadataDevice device : devices) { if (device.getProductType().equals("MiniES 3-3")) { writeDataToOldMiniMonitoring(device, data); break; @@ -244,7 +244,7 @@ private void writeData(int deviceId, TreeBasedTable data) * @param device * @param data */ - private void writeDataToOldMiniMonitoring(MetadataDevice device, TreeBasedTable data) { + private void writeDataToOldMiniMonitoring(OLD_MetadataDevice device, TreeBasedTable data) { int deviceId = device.getIdOpt().orElse(0); BatchPoints batchPoints = BatchPoints.database(database) // .tag("fems", String.valueOf(deviceId)) // diff --git a/io.openems.backend.openemswebsocket.api/.classpath b/io.openems.backend.uiwebsocket.api/.classpath similarity index 100% rename from io.openems.backend.openemswebsocket.api/.classpath rename to io.openems.backend.uiwebsocket.api/.classpath diff --git a/io.openems.backend.openemswebsocket.api/.gitignore b/io.openems.backend.uiwebsocket.api/.gitignore similarity index 100% rename from io.openems.backend.openemswebsocket.api/.gitignore rename to io.openems.backend.uiwebsocket.api/.gitignore diff --git a/io.openems.backend.openemswebsocket.api/.project b/io.openems.backend.uiwebsocket.api/.project similarity index 90% rename from io.openems.backend.openemswebsocket.api/.project rename to io.openems.backend.uiwebsocket.api/.project index 2a905468f0f..6c0d04fc084 100644 --- a/io.openems.backend.openemswebsocket.api/.project +++ b/io.openems.backend.uiwebsocket.api/.project @@ -1,6 +1,6 @@ - io.openems.backend.openemswebsocket.api + io.openems.backend.uiwebsocket.api diff --git a/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..49f4da409cf --- /dev/null +++ b/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java=UTF-8 +encoding//src/io/openems/backend/uiwebsocket/api/package-info.java=UTF-8 +encoding//test/.gitignore=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.openemswebsocket.api/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.uiwebsocket.api/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.browserwebsocket.api/bnd.bnd b/io.openems.backend.uiwebsocket.api/bnd.bnd similarity index 83% rename from io.openems.backend.browserwebsocket.api/bnd.bnd rename to io.openems.backend.uiwebsocket.api/bnd.bnd index 89805b5f67f..21b6fc4ea60 100644 --- a/io.openems.backend.browserwebsocket.api/bnd.bnd +++ b/io.openems.backend.uiwebsocket.api/bnd.bnd @@ -4,8 +4,7 @@ Bundle-Version: 1.0.0.${tstamp} -Export-Package: \ - io.openems.backend.browserwebsocket.api +Export-Package: io.openems.backend.uiwebsocket.api Require-Capability: \ compile-only diff --git a/io.openems.backend.browserwebsocket.api/readme.md b/io.openems.backend.uiwebsocket.api/readme.md similarity index 100% rename from io.openems.backend.browserwebsocket.api/readme.md rename to io.openems.backend.uiwebsocket.api/readme.md diff --git a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java new file mode 100644 index 00000000000..80f82ff3dd1 --- /dev/null +++ b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java @@ -0,0 +1,8 @@ +package io.openems.backend.uiwebsocket.api; + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface UiWebsocketService { + +} diff --git a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/package-info.java similarity index 50% rename from io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java rename to io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/package-info.java index ddaf51aa0dc..1f532951301 100644 --- a/io.openems.backend.openemswebsocket.api/src/io/openems/backend/openemswebsocket/api/package-info.java +++ b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/package-info.java @@ -1,2 +1,2 @@ @org.osgi.annotation.versioning.Version("1.0.0") -package io.openems.backend.openemswebsocket.api; +package io.openems.backend.uiwebsocket.api; diff --git a/io.openems.backend.openemswebsocket.api/test/.gitignore b/io.openems.backend.uiwebsocket.api/test/.gitignore similarity index 100% rename from io.openems.backend.openemswebsocket.api/test/.gitignore rename to io.openems.backend.uiwebsocket.api/test/.gitignore diff --git a/io.openems.backend.openemswebsocket.impl.provider/.classpath b/io.openems.backend.uiwebsocket.impl.provider/.classpath similarity index 100% rename from io.openems.backend.openemswebsocket.impl.provider/.classpath rename to io.openems.backend.uiwebsocket.impl.provider/.classpath diff --git a/io.openems.backend.openemswebsocket.impl.provider/.gitignore b/io.openems.backend.uiwebsocket.impl.provider/.gitignore similarity index 100% rename from io.openems.backend.openemswebsocket.impl.provider/.gitignore rename to io.openems.backend.uiwebsocket.impl.provider/.gitignore diff --git a/io.openems.backend.openemswebsocket.impl.provider/.project b/io.openems.backend.uiwebsocket.impl.provider/.project similarity index 88% rename from io.openems.backend.openemswebsocket.impl.provider/.project rename to io.openems.backend.uiwebsocket.impl.provider/.project index d77c8f344fc..24f2c28886a 100644 --- a/io.openems.backend.openemswebsocket.impl.provider/.project +++ b/io.openems.backend.uiwebsocket.impl.provider/.project @@ -1,6 +1,6 @@ - io.openems.backend.openemswebsocket.impl.provider + io.openems.backend.uiwebsocket.impl.provider diff --git a/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..183a6298baa --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java=UTF-8 +encoding//test/io/openems/backend/uiwebsocket/impl/provider/ProviderImplTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.browserwebsocket.impl.provider.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.openemswebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd new file mode 100644 index 00000000000..09fd56d71d6 --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd @@ -0,0 +1,26 @@ +# +# io.openems.backend.browserwebsocket.impl.provider PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: io.openems.backend.uiwebsocket.api + +Private-Package: io.openems.backend.uiwebsocket.impl.provider + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.wrapper.websocket;version=latest,\ + com.google.guava,\ + com.google.gson,\ + io.openems.common;version=latest,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.backend.uiwebsocket.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.browserwebsocket.impl.provider/debug.bndrun b/io.openems.backend.uiwebsocket.impl.provider/debug.bndrun similarity index 100% rename from io.openems.backend.browserwebsocket.impl.provider/debug.bndrun rename to io.openems.backend.uiwebsocket.impl.provider/debug.bndrun diff --git a/io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun b/io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun similarity index 60% rename from io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun rename to io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun index 918a8877244..16511578d5c 100644 --- a/io.openems.backend.browserwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun +++ b/io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun @@ -8,13 +8,18 @@ Bundle-SymbolicName: io.openems.backend.browserwebsocket.impl.provider.launch JPM-Command: provider --runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.browserwebsocket.impl.provider)' +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.uiwebsocket.impl.provider)' -runbundles: \ - io.openems.backend.browserwebsocket.impl.provider;version=snapshot,\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - slf4j.api;version='[1.8.0,1.8.1)' + slf4j.api;version='[1.8.0,1.8.1)',\ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.backend.metadata.odoo.provider;version=snapshot,\ + io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot diff --git a/io.openems.backend.browserwebsocket.impl.provider/readme.md b/io.openems.backend.uiwebsocket.impl.provider/readme.md similarity index 100% rename from io.openems.backend.browserwebsocket.impl.provider/readme.md rename to io.openems.backend.uiwebsocket.impl.provider/readme.md diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java new file mode 100644 index 00000000000..c04dce1bad4 --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java @@ -0,0 +1,188 @@ +package io.openems.backend.uiwebsocket.impl.provider; + +import java.net.InetSocketAddress; +import java.util.Iterator; +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import io.openems.common.utils.JsonUtils; + +public abstract class AbstractWebsocketServer extends WebSocketServer { + private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); + + protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, + Optional deviceNameOpt); + + protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); + + protected abstract void _onError(WebSocket websocket, Exception ex); + + protected abstract void _onClose(WebSocket websocket); + + public AbstractWebsocketServer(int port) { + super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); + } + + @Override + public final void onStart() { + // nothing to do + } + + /** + * Open event of websocket. + */ + @Override + public final void onOpen(WebSocket websocket, ClientHandshake handshake) { + try { + this._onOpen(websocket, handshake); + } catch (Throwable e) { + log.error("onOpen-Error [" + this.handshakeToJsonObject(handshake) + "]: "); + e.printStackTrace(); + } + } + + /** + * Message event of websocket. Handles a new message. + */ + @Override + public final void onMessage(WebSocket websocket, String message) { + try { + JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); + Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); + Optional deviceNameOpt = JsonUtils.getAsOptionalString(jMessage, "device"); + this._onMessage(websocket, jMessage, jMessageIdOpt, deviceNameOpt); + } catch (Throwable e) { + log.error("onMessage-Error [" + message + "]: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Close event of websocket. Removes the websocket. Keeps the session. Calls + * _onClose() + */ + @Override + public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { + // try { + // Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); + // String sessionString; + // if (sessionOpt.isPresent()) { + // sessionString = sessionOpt.get().toString() + " "; + // } else { + // sessionString = ""; + // } + // log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + + // reason + "]"); + // this.websockets.remove(websocket); + this._onClose(websocket); + // } catch (Throwable e) { + // log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + + // e.getMessage()); + // } + } + + /** + * Error event of websocket. Logs the error. + */ + @Override + public final void onError(WebSocket websocket, Exception ex) { + // S session = this.websockets.get(websocket); + // String sessionString; + // if (session == null) { + // sessionString = ""; + // } else { + // sessionString = session.toString(); + // } + // log.warn(sessionString + " Websocket error: " + ex.getMessage()); + this._onError(websocket, ex); + } + + /** + * Get cookie from handshake + * + * @param handshake + * @return cookie as JsonObject + */ + protected Optional getSessionIdFromHandshake(ClientHandshake handshake) { + JsonObject jCookie = new JsonObject(); + if (handshake.hasFieldValue("cookie")) { + String cookieString = handshake.getFieldValue("cookie"); + for (String cookieVariable : cookieString.split("; ")) { + String[] keyValue = cookieVariable.split("="); + if (keyValue.length == 2) { + jCookie.addProperty(keyValue[0], keyValue[1]); + } + } + } + return JsonUtils.getAsOptionalString(jCookie, "session_id"); + } + + /** + * Converts a Handshake to a JsonObject + * + * @param handshake + * @return + */ + protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { + JsonObject j = new JsonObject(); + for (Iterator iter = handshake.iterateHttpFields(); iter.hasNext();) { + String field = iter.next(); + j.addProperty(field, handshake.getFieldValue(field)); + } + return j; + } + + /** + * Returns the Websocket for the given token + * + * @param name + * @return + */ +// public Optional getWebsocketByToken(String token) { +// Optional sessionOpt = (Optional) this.sessionManager.getSessionByToken(token); +// if (!sessionOpt.isPresent()) { +// return Optional.empty(); +// } +// S session = sessionOpt.get(); +// return Optional.ofNullable(this.websockets.inverse().get(session)); +// } +// +// protected void addWebsocket(WebSocket websocket, S session) { +// synchronized (this.websockets) { +// if (this.websockets.containsKey(websocket)) { +// log.warn("Websocket [" + websocket + "] already existed. Replacing existing one with session [" +// + session + "]"); +// } +// if (this.websockets.inverse().containsKey(session)) { +// log.warn("Session [" + session + "] already existed. Replacing existing one with websocket [" +// + websocket + "]"); +// } +// this.websockets.forcePut(websocket, session); +// } +// } +// +// protected void removeWebsocket(WebSocket websocket) { +// synchronized (this.websockets) { +// this.websockets.remove(websocket); +// } +// } +// +// protected Optional getSessionFromWebsocket(WebSocket websocket) { +// return Optional.ofNullable(this.websockets.get(websocket)); +// } +// +// protected Optional getWebsocketFromSession(S session) { +// return Optional.ofNullable(this.websockets.inverse().get(session)); +// } +} diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java new file mode 100644 index 00000000000..9885e5ae775 --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -0,0 +1,76 @@ +package io.openems.backend.uiwebsocket.impl.provider; + +import java.io.IOException; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.uiwebsocket.api.UiWebsocketService; + +import org.osgi.service.metatype.annotations.Designate; + + +@Designate( ocd=UiWebsocket.Config.class, factory=true) +@Component(name="UiWebsocket") +public class UiWebsocket implements UiWebsocketService { + + private final Logger log = LoggerFactory.getLogger(UiWebsocket.class); + + private UiWebsocketServer server = null; + + @Reference + private MetadataService metadataService; + + protected MetadataService getMetadataService() { + return metadataService; + } + + @ObjectClassDefinition + @interface Config { + int port(); + } + + @Activate + void activate(Config config) { + log.debug("Activate UiWebsocket [port=" + config.port() + "]"); + + this.stopServer(); + this.startServer(config.port()); + + } + + @Deactivate + void deactivate() { + log.debug("Deactivate UiWebsocket"); + this.stopServer(); + } + + /** + * Stop existing websocket server + */ + private void stopServer() { + if(this.server != null) { + try { + this.server.stop(); + } catch (IOException | InterruptedException e) { + log.error("Unable to stop existing UiWebsocketServer: " + e.getMessage()); + } + } + } + + /** + * Create and start new server + * + * @param port + */ + private void startServer(int port) { + this.server = new UiWebsocketServer(this, port); + this.server.start(); + } +} diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java new file mode 100644 index 00000000000..f97d992f648 --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -0,0 +1,94 @@ +package io.openems.backend.uiwebsocket.impl.provider; + +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.metadata.api.UserDevicesInfo; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; + +public class UiWebsocketServer extends AbstractWebsocketServer { + + private final Logger log = LoggerFactory.getLogger(UiWebsocketServer.class); + private final UiWebsocket parent; + + public UiWebsocketServer(UiWebsocket parent, int port) { + super(port); + this.parent = parent; + } + + @Override + protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + String error = ""; + + // login using session_id from the cookie + Optional sessionIdOpt = getSessionIdFromHandshake(handshake); + if(!sessionIdOpt.isPresent()) { + error = "Session-ID is missing in handshake"; + } else { + UserDevicesInfo info; + try { + info = this.parent.getMetadataService().getInfoWithSession(sessionIdOpt.get()); + System.out.println(info.getUserName()); + } catch (OpenemsException e) { + error = e.getMessage(); + } + } + System.out.println(error); + +// // check if the session is now valid and send reply to browser +// BrowserSessionData data = session.getData(); +// if (error.isEmpty()) { +// // add isOnline information +// OpenemsWebsocketSingleton openemsWebsocket = OpenemsWebsocket.instance(); +// for (DeviceImpl device : data.getDevices()) { +// device.setOnline(openemsWebsocket.isOpenemsWebsocketConnected(device.getName())); +// } +// +// // send connection successful to browser +// JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), Optional.empty(), +// data.getDevices()); +// // TODO write user name to log output +// WebSocketUtils.send(websocket, jReply); +// +// // add websocket to local cache +// this.addWebsocket(websocket, session); +// +// log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") +// + "]."); +// +// } else { +// // send connection failed to browser +// JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); +// WebSocketUtils.send(websocket, jReply); +// log.warn("User [" + data.getUserName() + "] connection failed. Session [" +// + data.getOdooSessionId().orElse("") + "] Error [" + error + "]."); +// +// websocket.closeConnection(CloseFrame.REFUSE, error); +// } + } + + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, + Optional deviceNameOpt) { + log.info("UiWebsocketServer: On Message"); + } + + @Override + protected void _onError(WebSocket websocket, Exception ex) { + log.info("UiWebsocketServer: On Error"); + } + + @Override + protected void _onClose(WebSocket websocket) { + log.info("UiWebsocketServer: On Close"); + } +} diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/package-info.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/package-info.java new file mode 100644 index 00000000000..d44080ad204 --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.uiwebsocket.impl.provider; diff --git a/io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java b/io.openems.backend.uiwebsocket.impl.provider/test/io/openems/backend/uiwebsocket/impl/provider/ProviderImplTest.java similarity index 56% rename from io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java rename to io.openems.backend.uiwebsocket.impl.provider/test/io/openems/backend/uiwebsocket/impl/provider/ProviderImplTest.java index 834eab1936e..6d3e974d7e6 100644 --- a/io.openems.backend.browserwebsocket.impl.provider/test/io/openems/backend/browserwebsocket/impl/provider/ProviderImplTest.java +++ b/io.openems.backend.uiwebsocket.impl.provider/test/io/openems/backend/uiwebsocket/impl/provider/ProviderImplTest.java @@ -1,10 +1,10 @@ -package io.openems.backend.browserwebsocket.impl.provider; +package io.openems.backend.uiwebsocket.impl.provider; import static org.junit.Assert.assertNotNull; import org.junit.Test; -import io.openems.backend.browserwebsocket.impl.provider.BrowserWebsocket; +import io.openems.backend.uiwebsocket.impl.provider.UiWebsocket; /* * Example JUNit test case @@ -19,7 +19,7 @@ public class ProviderImplTest { @Test public void simple() { - BrowserWebsocket impl = new BrowserWebsocket(); + UiWebsocket impl = new UiWebsocket(); assertNotNull(impl); } diff --git a/io.openems.wrapper/jar/Java-WebSocket-1.3.6.jar b/io.openems.wrapper/jar/Java-WebSocket-1.3.6.jar deleted file mode 100644 index 072633aed0d2e5dd963636ee1343f68dda260409..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108198 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%0D{Quu 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 Oe2pP)7PliucKrvPTmS9= literal 0 HcmV?d00001 diff --git a/io.openems.wrapper/websocket.bnd b/io.openems.wrapper/websocket.bnd index 0fc8ad9ab6e..7d4f6fa14da 100644 --- a/io.openems.wrapper/websocket.bnd +++ b/io.openems.wrapper/websocket.bnd @@ -8,12 +8,12 @@ Bundle-Description: This repository contains a barebones WebSocket server and cl in 100% Java. The underlying classes are implemented using the Java ServerSocketChannel and SocketChannel \ classes, which allows for a non-blocking event-driven model (similar to the WebSocket API for web browsers). \ Implemented WebSocket protocol versions are: Hixie 75, Hixie 76, Hybi 10, and Hybi 17 -Bundle-Version: 0.0.0.notdefined +Bundle-Version: 1.3.7 -Include-Resource: @jar/Java-WebSocket-1.3.6.jar +Include-Resource: @jar/Java-WebSocket-1.3.7.jar -exportcontents: org.java_websocket, org.java_websocket.drafts, org.java_websocket.exceptions, org.java_websocket.handshake, org.java_websocket.server -sources: false -Bundle-Version: 1.3.6 \ No newline at end of file +Bundle-Version: 1.3.7 \ No newline at end of file diff --git a/ui/src/environments/environment.ts b/ui/src/environments/environment.ts index 83005bbd13a..7d980e24d10 100644 --- a/ui/src/environments/environment.ts +++ b/ui/src/environments/environment.ts @@ -3,7 +3,7 @@ import { DefaultTypes } from "../app/shared/service/defaulttypes"; class DefaultEnvironment extends Environment { public readonly production = false; - public readonly url = "ws://" + location.hostname + ":8085"; + public readonly url = "ws://" + location.hostname + ":8088"; public readonly backend: DefaultTypes.Backend = "OpenEMS Edge"; public debugMode = true; } From 3471fe13efd5644b9e2f119ebb720db4c1b696b0 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Feb 2018 21:14:58 +0100 Subject: [PATCH 029/156] Add WebSocket sources --- .../jar/Java-WebSocket-1.3.7-sources.jar | Bin 0 -> 124225 bytes io.openems.wrapper/websocket.bnd | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 io.openems.wrapper/jar/Java-WebSocket-1.3.7-sources.jar diff --git a/io.openems.wrapper/jar/Java-WebSocket-1.3.7-sources.jar b/io.openems.wrapper/jar/Java-WebSocket-1.3.7-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..02e74b0493f137666c375330b7bc517fab790054 GIT binary patch literal 124225 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

      Ox&+T38%VO6_ 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

      n_rW}_rzOWxo-E4Lt4m{Ghe)~MVujZ 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

      `uH$5;0_ZJX;(^d_OBM`2OkiO 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 literal 0 HcmV?d00001 diff --git a/io.openems.wrapper/websocket.bnd b/io.openems.wrapper/websocket.bnd index 7d4f6fa14da..92068295f39 100644 --- a/io.openems.wrapper/websocket.bnd +++ b/io.openems.wrapper/websocket.bnd @@ -10,7 +10,7 @@ Bundle-Description: This repository contains a barebones WebSocket server and cl Implemented WebSocket protocol versions are: Hixie 75, Hixie 76, Hybi 10, and Hybi 17 Bundle-Version: 1.3.7 -Include-Resource: @jar/Java-WebSocket-1.3.7.jar +Include-Resource: @jar/Java-WebSocket-1.3.7.jar, OSGI-OPT/src=@jar/Java-WebSocket-1.3.7-sources.jar -exportcontents: org.java_websocket, org.java_websocket.drafts, org.java_websocket.exceptions, org.java_websocket.handshake, org.java_websocket.server From fe0d1696628f0fe77d28c773d84eab31808690f9 Mon Sep 17 00:00:00 2001 From: Hueseyin Sahutoglu Date: Wed, 7 Feb 2018 14:33:06 +0100 Subject: [PATCH 030/156] Checked empty array in ThingsRepository --- edge/src/io/openems/core/ThingRepository.java | 1130 +++++++++-------- .../impl/device/commercial/WarningEss.java | 61 +- 2 files changed, 615 insertions(+), 576 deletions(-) diff --git a/edge/src/io/openems/core/ThingRepository.java b/edge/src/io/openems/core/ThingRepository.java index aa743e4de0d..08f427dc403 100644 --- a/edge/src/io/openems/core/ThingRepository.java +++ b/edge/src/io/openems/core/ThingRepository.java @@ -1,565 +1,567 @@ -/******************************************************************************* - * 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.core; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Table; -import com.google.gson.JsonObject; - -import io.openems.api.bridge.Bridge; -import io.openems.api.channel.Channel; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.Controller; -import io.openems.api.device.Device; -import io.openems.api.device.nature.DeviceNature; -import io.openems.api.doc.ChannelDoc; -import io.openems.api.doc.ThingDoc; -import io.openems.api.exception.OpenemsException; -import io.openems.api.exception.ReflectionException; -import io.openems.api.persistence.Persistence; -import io.openems.api.persistence.QueryablePersistence; -import io.openems.api.scheduler.Scheduler; -import io.openems.api.thing.Thing; -import io.openems.api.thing.ThingChannelsUpdatedListener; -import io.openems.common.types.ChannelAddress; -import io.openems.core.ThingsChangedListener.Action; -import io.openems.core.utilities.ConfigUtils; -import io.openems.core.utilities.InjectionUtils; -import io.openems.core.utilities.JsonUtils; - -public class ThingRepository implements ThingChannelsUpdatedListener { - private final static Logger log = LoggerFactory.getLogger(ThingRepository.class); - - private static ThingRepository instance; - - public static synchronized ThingRepository getInstance() { - if (ThingRepository.instance == null) { - ThingRepository.instance = new ThingRepository(); - } - return ThingRepository.instance; - } - - private ThingRepository() { - classRepository = ClassRepository.getInstance(); - } - - private final ClassRepository classRepository; - private final BiMap thingIds = HashBiMap.create(); - private HashMultimap, Thing> thingClasses = HashMultimap.create(); - private Set bridges = new HashSet<>(); - // TODO scheduler should not be a set, but only one value - private Set schedulers = new HashSet<>(); - private Set persistences = new HashSet<>(); - private Set queryablePersistences = new HashSet<>(); - private Set deviceNatures = new HashSet<>(); - private final Table thingChannels = HashBasedTable.create(); - private HashMultimap> thingConfigChannels = HashMultimap.create(); - private HashMultimap> thingWriteChannels = HashMultimap.create(); - private List thingListeners = new LinkedList<>(); - - public void addThingChangedListener(ThingsChangedListener listener) { - this.thingListeners.add(listener); - } - - public void removeThingChangedListener(ThingsChangedListener listener) { - this.thingListeners.remove(listener); - } - - /** - * Add a Thing to the Repository and cache its Channels and other information for later usage. - * - * @param thing - */ - public synchronized void addThing(Thing thing) { - if (thingIds.containsValue(thing)) { - // Thing was already added - return; - } - // Add to thingIds - thingIds.forcePut(thing.id(), thing); - - // Add to thingClasses - thingClasses.put(thing.getClass(), thing); - - // Add to bridges - if (thing instanceof Bridge) { - bridges.add((Bridge) thing); - } - - // Add to schedulers - if (thing instanceof Scheduler) { - schedulers.add((Scheduler) thing); - } - - // Add to persistences - if (thing instanceof Persistence) { - persistences.add((Persistence) thing); - } - - // Add to queryablePersistences - if (thing instanceof QueryablePersistence) { - queryablePersistences.add((QueryablePersistence) thing); - } - - // Add to device natures - if (thing instanceof DeviceNature) { - deviceNatures.add((DeviceNature) thing); - } - - // Add Listener - thing.addListener(this); - - // Apply channel annotation (this happens now and again after initializing the thing via init() - this.applyChannelAnnotation(thing); - - // Add Channels thingConfigChannels - ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass()); - for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) { - Member member = channelDoc.getMember(); - try { - List channels = new ArrayList<>(); - java.util.function.Consumer addToChannels = (c) -> { - if(c == null) { - log.error( - "Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]"); - } else { - channels.add(c); - } - }; - if (member instanceof Method) { - if (((Method) member).getReturnType().isArray()) { - Channel[] ch = (Channel[]) ((Method) member).invoke(thing); - for (Channel c : ch) { - addToChannels.accept(c); - } - } else { - // It's a Method with ReturnType Channel - Channel c = (Channel) ((Method) member).invoke(thing); - addToChannels.accept(c); - } - } else if (member instanceof Field) { - // It's a Field with Type Channel - Channel c = (Channel) ((Field) member).get(thing); - addToChannels.accept(c); - } else { - continue; - } - if (channels.isEmpty()) { - continue; - } - for (Channel channel : channels) { - // Add Channel to thingChannels - thingChannels.put(thing, channel.id(), channel); - if (channel instanceof ConfigChannel) { - - // Add Channel to configChannels - thingConfigChannels.put(thing, (ConfigChannel) channel); - } - } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - log.warn("Unable to add Channel. Member [" + member.getName() + "]", e); - } - } - for (ThingsChangedListener listener : thingListeners) { - listener.thingChanged(thing, Action.ADD); - } - } - - public void applyChannelAnnotation(Thing thing) { - ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass()); - for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) { - try { - Channel channel = getChannel(thing, channelDoc.getMember()); - channel.setChannelDoc(channelDoc); - } catch (OpenemsException e) { - log.debug(e.getMessage()); - } - } - } - - /** - * Remove a Thing from the Repository. - * - * @param thing - */ - public synchronized void removeThing(String thingId) { - Thing thing = thingIds.get(thingId); - removeThing(thing); - } - - /** - * Remove a Thing from the Repository. - * - * @param thing - */ - public synchronized void removeThing(Thing thing) { - // Remove from thingIds - thingIds.remove(thing.id()); - - // Remove from thingClasses - thingClasses.remove(thing.getClass(), thing); - - // Remove from bridges - if (thing instanceof Bridge) { - bridges.remove(thing); - } - - // Remove from schedulers - if (thing instanceof Scheduler) { - schedulers.remove(thing); - } - - // Remove from persistences - if (thing instanceof Persistence) { - persistences.remove(thing); - } - - // Remove from queryablePersistences - if (thing instanceof QueryablePersistence) { - queryablePersistences.remove(thing); - } - - // Remove from deviceNatures - if (thing instanceof DeviceNature) { - deviceNatures.remove(thing); - } - - // Remove controller - if (thing instanceof Controller) { - Controller controller = (Controller) thing; - for (Scheduler scheduler : getSchedulers()) { - scheduler.removeController(controller); - } - } - - // Remove device - if (thing instanceof Device) { - for (Bridge bridge : bridges) { - bridge.removeDevice((Device) thing); - } - } - - // Remove Listener - thing.removeListener(this); - // TODO further cleaning if required - for (ThingsChangedListener listener : thingListeners) { - listener.thingChanged(thing, Action.REMOVE); - } - } - - public Thing getThing(String thingId) { - Thing thing = thingIds.get(thingId); - return thing; - } - - public Set getThings() { - return Collections.unmodifiableSet(this.thingIds.values()); - } - - /** - * Returns all Channels for this Thing. - * - * @param thing - * @return - */ - public synchronized Collection getChannels(Thing thing) { - return Collections.unmodifiableCollection(thingChannels.row(thing).values()); - } - - /** - * Returns all Config-Channels. - * - * @return - */ - public synchronized Collection> getConfigChannels() { - return Collections.unmodifiableCollection(thingConfigChannels.values()); - } - - /** - * Returns all Config-Channels for this Thing. - * - * @param thing - * @return - */ - public synchronized Set> getConfigChannels(Thing thing) { - return Collections.unmodifiableSet(thingConfigChannels.get(thing)); - } - - /** - * Returns all Write-Channels for this Thing. - * - * @param thing - * @return - */ - public synchronized Set> getWriteChannels(Thing thing) { - return Collections.unmodifiableSet(thingWriteChannels.get(thing)); - } - - /** - * Returns all Write-Channels. - * - * @param thing - * @return - */ - public synchronized Collection> getWriteChannels() { - return Collections.unmodifiableCollection(thingWriteChannels.values()); - } - - /** - * Returns all Persistence-Workers. - * - * @param thing - * @return - */ - public synchronized Set getPersistences() { - return Collections.unmodifiableSet(persistences); - } - - /** - * Returns the ChannelDoc for a given Channel - * - * @param channelAddress - * @return - */ - public synchronized Optional getChannelDoc(ChannelAddress channelAddress) { - Thing thing = getThing(channelAddress.getThingId()); - if (thing == null) { - return Optional.empty(); - } - ThingDoc thingDoc = ClassRepository.getInstance().getThingDoc(thing.getClass()); - Optional channelDoc = thingDoc.getChannelDoc(channelAddress.getChannelId()); - return channelDoc; - } - - public synchronized Set getQueryablePersistences() { - return Collections.unmodifiableSet(queryablePersistences); - } - - public synchronized Set> getThingClasses() { - return Collections.unmodifiableSet(thingClasses.keySet()); - } - - public synchronized Set getThingsByClass(Class clazz) { - return Collections.unmodifiableSet(thingClasses.get(clazz)); - } - - public synchronized Set getThingsAssignableByClass(Class clazz) { - Set things = new HashSet<>(); - for (Class subclazz : thingClasses.keySet()) { - if (clazz.isAssignableFrom(subclazz)) { - things.addAll(thingClasses.get(subclazz)); - } - - } - return Collections.unmodifiableSet(things); - } - - public synchronized Optional getThingById(String id) { - return Optional.ofNullable(thingIds.get(id)); - } - - public synchronized Set getBridges() { - return Collections.unmodifiableSet(bridges); - } - - public synchronized Set getSchedulers() { - return Collections.unmodifiableSet(schedulers); - } - - public synchronized Set getDeviceNatures() { - return Collections.unmodifiableSet(deviceNatures); - } - - public Optional getChannel(String thingId, String channelId) { - Thing thing = thingIds.get(thingId); - if (thing == null) { - return Optional.empty(); - } - Map channels = thingChannels.row(thing); - Channel channel = channels.get(channelId); - return Optional.ofNullable(channel); - } - - public Optional getChannel(ChannelAddress channelAddress) { - return this.getChannel(channelAddress.getThingId(), channelAddress.getChannelId()); - } - - public Optional getChannelByAddress(String address) { - try { - return getChannel(ChannelAddress.fromString(address)); - } catch (io.openems.common.exceptions.OpenemsException e) { - return Optional.empty(); - } - } - - public Controller createController(JsonObject jController) throws ReflectionException { - String controllerClass = JsonUtils.getAsString(jController, "class"); - Controller controller; - if (jController.has("id")) { - String id = JsonUtils.getAsString(jController, "id"); - controller = (Controller) InjectionUtils.getThingInstance(controllerClass, id); - } else { - controller = (Controller) InjectionUtils.getThingInstance(controllerClass); - } - log.info("Add Controller[" + controller.id() + "], Implementation[" + controller.getClass().getSimpleName() - + "]"); - this.addThing(controller); - ConfigUtils.injectConfigChannels(this.getConfigChannels(controller), jController); - return controller; - } - - public Device createDevice(JsonObject jDevice, Bridge parent) throws ReflectionException { - String deviceClass = JsonUtils.getAsString(jDevice, "class"); - Device device = (Device) InjectionUtils.getThingInstance(deviceClass, parent); - log.info("Add Device[" + device.id() + "], Implementation[" + device.getClass().getSimpleName() + "]"); - this.addThing(device); - // instantiate DeviceNatures with Device reference - ConfigUtils.injectConfigChannels(this.getConfigChannels(device), jDevice, device); - return device; - } - - @Override - public void thingChannelsUpdated(Thing thing) { - // remove Channels from thingChannels, thingWriteChannels - Databus databus = Databus.getInstance(); - Set> thingRow = thingChannels.row(thing).entrySet(); - Iterator> i = thingRow.iterator(); - while (i.hasNext()) { - Entry thingChannel = i.next(); - if (!(thingChannel.getValue() instanceof ConfigChannel)) { - thingChannel.getValue().removeChangeListener(databus); - thingChannel.getValue().removeUpdateListener(databus); - i.remove(); - } - } - thingWriteChannels.removeAll(thing); - - // Add Channels to thingChannels, thingConfigChannels and thingWriteChannels - ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass()); - for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) { - Member member = channelDoc.getMember(); - try { - List channels = new ArrayList<>(); - if (member instanceof Method) { - if (((Method) member).getReturnType().isArray()) { - Channel[] ch = (Channel[]) ((Method) member).invoke(thing); - for (Channel c : ch) { - channels.add(c); - } - } else { - // It's a Method with ReturnType Channel - channels.add((Channel) ((Method) member).invoke(thing)); - } - } else if (member instanceof Field) { - // It's a Field with Type Channel - channels.add((Channel) ((Field) member).get(thing)); - } else { - continue; - } - if (channels.isEmpty()) { - log.error( - "Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]"); - continue; - } - for (Channel channel : channels) { - if (channel != null) { - // Add Channel to thingChannels - thingChannels.put(thing, channel.id(), channel); - - // Add Channel to writeChannels - if (channel instanceof WriteChannel) { - thingWriteChannels.put(thing, (WriteChannel) channel); - } - - // Register Databus as listener - if (channel instanceof ReadChannel) { - ((ReadChannel) channel).addUpdateListener(databus); - ((ReadChannel) channel).addChangeListener(databus); - } - } - } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - log.warn("Unable to add Channel. Member [" + member.getName() + "]", e); - } - } - } - - /** - * Gets the channel behind Thing member - * - * @param thing - * @param member - * @return - * @throws OpenemsException - */ - private Channel getChannel(Thing thing, Member member) throws OpenemsException { - Object channelObj; - if (member instanceof Field) { - Field f = (Field) member; - try { - channelObj = f.get(thing); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new OpenemsException( - "Unable to get Channel. Thing [" + thing.id() + "] Field [" + f.getName() + "]"); - } - } else { - Method m = (Method) member; - try { - channelObj = m.invoke(thing, new Object[0]); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new OpenemsException( - "Unable to get Channel. Thing [" + thing.id() + "] Method [" + m.getName() + "]"); - } - } - if (channelObj == null) { - throw new OpenemsException("Channel is null. Thing [" + thing.id() + "] Member [" + member.getName() + "]"); - } - if (!(channelObj instanceof Channel)) { - throw new OpenemsException("This is not a channel. Thing [" + thing.id() + "] Member [" + member.getName() - + "] Channel [" + channelObj + "]"); - } - return (Channel) channelObj; - } +/******************************************************************************* + * 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.core; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Table; +import com.google.gson.JsonObject; + +import io.openems.api.bridge.Bridge; +import io.openems.api.channel.Channel; +import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.Controller; +import io.openems.api.device.Device; +import io.openems.api.device.nature.DeviceNature; +import io.openems.api.doc.ChannelDoc; +import io.openems.api.doc.ThingDoc; +import io.openems.api.exception.OpenemsException; +import io.openems.api.exception.ReflectionException; +import io.openems.api.persistence.Persistence; +import io.openems.api.persistence.QueryablePersistence; +import io.openems.api.scheduler.Scheduler; +import io.openems.api.thing.Thing; +import io.openems.api.thing.ThingChannelsUpdatedListener; +import io.openems.common.types.ChannelAddress; +import io.openems.core.ThingsChangedListener.Action; +import io.openems.core.utilities.ConfigUtils; +import io.openems.core.utilities.InjectionUtils; +import io.openems.core.utilities.JsonUtils; + +public class ThingRepository implements ThingChannelsUpdatedListener { + private final static Logger log = LoggerFactory.getLogger(ThingRepository.class); + + private static ThingRepository instance; + + public static synchronized ThingRepository getInstance() { + if (ThingRepository.instance == null) { + ThingRepository.instance = new ThingRepository(); + } + return ThingRepository.instance; + } + + private ThingRepository() { + classRepository = ClassRepository.getInstance(); + } + + private final ClassRepository classRepository; + private final BiMap thingIds = HashBiMap.create(); + private HashMultimap, Thing> thingClasses = HashMultimap.create(); + private Set bridges = new HashSet<>(); + // TODO scheduler should not be a set, but only one value + private Set schedulers = new HashSet<>(); + private Set persistences = new HashSet<>(); + private Set queryablePersistences = new HashSet<>(); + private Set deviceNatures = new HashSet<>(); + private final Table thingChannels = HashBasedTable.create(); + private HashMultimap> thingConfigChannels = HashMultimap.create(); + private HashMultimap> thingWriteChannels = HashMultimap.create(); + private List thingListeners = new LinkedList<>(); + + public void addThingChangedListener(ThingsChangedListener listener) { + this.thingListeners.add(listener); + } + + public void removeThingChangedListener(ThingsChangedListener listener) { + this.thingListeners.remove(listener); + } + + /** + * Add a Thing to the Repository and cache its Channels and other information for later usage. + * + * @param thing + */ + public synchronized void addThing(Thing thing) { + if (thingIds.containsValue(thing)) { + // Thing was already added + return; + } + // Add to thingIds + thingIds.forcePut(thing.id(), thing); + + // Add to thingClasses + thingClasses.put(thing.getClass(), thing); + + // Add to bridges + if (thing instanceof Bridge) { + bridges.add((Bridge) thing); + } + + // Add to schedulers + if (thing instanceof Scheduler) { + schedulers.add((Scheduler) thing); + } + + // Add to persistences + if (thing instanceof Persistence) { + persistences.add((Persistence) thing); + } + + // Add to queryablePersistences + if (thing instanceof QueryablePersistence) { + queryablePersistences.add((QueryablePersistence) thing); + } + + // Add to device natures + if (thing instanceof DeviceNature) { + deviceNatures.add((DeviceNature) thing); + } + + // Add Listener + thing.addListener(this); + + // Apply channel annotation (this happens now and again after initializing the thing via init() + this.applyChannelAnnotation(thing); + + // Add Channels thingConfigChannels + ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass()); + for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) { + Member member = channelDoc.getMember(); + try { + List channels = new ArrayList<>(); + java.util.function.Consumer addToChannels = (c) -> { + if(c == null) { + log.error( + "Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]"); + } else { + channels.add(c); + } + }; + if (member instanceof Method) { + if (((Method) member).getReturnType().isArray()) { + Channel[] ch = (Channel[]) ((Method) member).invoke(thing); + for (Channel c : ch) { + addToChannels.accept(c); + } + } else { + // It's a Method with ReturnType Channel + Channel c = (Channel) ((Method) member).invoke(thing); + addToChannels.accept(c); + } + } else if (member instanceof Field) { + // It's a Field with Type Channel + Channel c = (Channel) ((Field) member).get(thing); + addToChannels.accept(c); + } else { + continue; + } + if (channels.isEmpty()) { + continue; + } + for (Channel channel : channels) { + // Add Channel to thingChannels + thingChannels.put(thing, channel.id(), channel); + if (channel instanceof ConfigChannel) { + + // Add Channel to configChannels + thingConfigChannels.put(thing, (ConfigChannel) channel); + } + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + log.warn("Unable to add Channel. Member [" + member.getName() + "]", e); + } + } + for (ThingsChangedListener listener : thingListeners) { + listener.thingChanged(thing, Action.ADD); + } + } + + public void applyChannelAnnotation(Thing thing) { + ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass()); + for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) { + try { + Channel channel = getChannel(thing, channelDoc.getMember()); + channel.setChannelDoc(channelDoc); + } catch (OpenemsException e) { + log.debug(e.getMessage()); + } + } + } + + /** + * Remove a Thing from the Repository. + * + * @param thing + */ + public synchronized void removeThing(String thingId) { + Thing thing = thingIds.get(thingId); + removeThing(thing); + } + + /** + * Remove a Thing from the Repository. + * + * @param thing + */ + public synchronized void removeThing(Thing thing) { + // Remove from thingIds + thingIds.remove(thing.id()); + + // Remove from thingClasses + thingClasses.remove(thing.getClass(), thing); + + // Remove from bridges + if (thing instanceof Bridge) { + bridges.remove(thing); + } + + // Remove from schedulers + if (thing instanceof Scheduler) { + schedulers.remove(thing); + } + + // Remove from persistences + if (thing instanceof Persistence) { + persistences.remove(thing); + } + + // Remove from queryablePersistences + if (thing instanceof QueryablePersistence) { + queryablePersistences.remove(thing); + } + + // Remove from deviceNatures + if (thing instanceof DeviceNature) { + deviceNatures.remove(thing); + } + + // Remove controller + if (thing instanceof Controller) { + Controller controller = (Controller) thing; + for (Scheduler scheduler : getSchedulers()) { + scheduler.removeController(controller); + } + } + + // Remove device + if (thing instanceof Device) { + for (Bridge bridge : bridges) { + bridge.removeDevice((Device) thing); + } + } + + // Remove Listener + thing.removeListener(this); + // TODO further cleaning if required + for (ThingsChangedListener listener : thingListeners) { + listener.thingChanged(thing, Action.REMOVE); + } + } + + public Thing getThing(String thingId) { + Thing thing = thingIds.get(thingId); + return thing; + } + + public Set getThings() { + return Collections.unmodifiableSet(this.thingIds.values()); + } + + /** + * Returns all Channels for this Thing. + * + * @param thing + * @return + */ + public synchronized Collection getChannels(Thing thing) { + return Collections.unmodifiableCollection(thingChannels.row(thing).values()); + } + + /** + * Returns all Config-Channels. + * + * @return + */ + public synchronized Collection> getConfigChannels() { + return Collections.unmodifiableCollection(thingConfigChannels.values()); + } + + /** + * Returns all Config-Channels for this Thing. + * + * @param thing + * @return + */ + public synchronized Set> getConfigChannels(Thing thing) { + return Collections.unmodifiableSet(thingConfigChannels.get(thing)); + } + + /** + * Returns all Write-Channels for this Thing. + * + * @param thing + * @return + */ + public synchronized Set> getWriteChannels(Thing thing) { + return Collections.unmodifiableSet(thingWriteChannels.get(thing)); + } + + /** + * Returns all Write-Channels. + * + * @param thing + * @return + */ + public synchronized Collection> getWriteChannels() { + return Collections.unmodifiableCollection(thingWriteChannels.values()); + } + + /** + * Returns all Persistence-Workers. + * + * @param thing + * @return + */ + public synchronized Set getPersistences() { + return Collections.unmodifiableSet(persistences); + } + + /** + * Returns the ChannelDoc for a given Channel + * + * @param channelAddress + * @return + */ + public synchronized Optional getChannelDoc(ChannelAddress channelAddress) { + Thing thing = getThing(channelAddress.getThingId()); + if (thing == null) { + return Optional.empty(); + } + ThingDoc thingDoc = ClassRepository.getInstance().getThingDoc(thing.getClass()); + Optional channelDoc = thingDoc.getChannelDoc(channelAddress.getChannelId()); + return channelDoc; + } + + public synchronized Set getQueryablePersistences() { + return Collections.unmodifiableSet(queryablePersistences); + } + + public synchronized Set> getThingClasses() { + return Collections.unmodifiableSet(thingClasses.keySet()); + } + + public synchronized Set getThingsByClass(Class clazz) { + return Collections.unmodifiableSet(thingClasses.get(clazz)); + } + + public synchronized Set getThingsAssignableByClass(Class clazz) { + Set things = new HashSet<>(); + for (Class subclazz : thingClasses.keySet()) { + if (clazz.isAssignableFrom(subclazz)) { + things.addAll(thingClasses.get(subclazz)); + } + + } + return Collections.unmodifiableSet(things); + } + + public synchronized Optional getThingById(String id) { + return Optional.ofNullable(thingIds.get(id)); + } + + public synchronized Set getBridges() { + return Collections.unmodifiableSet(bridges); + } + + public synchronized Set getSchedulers() { + return Collections.unmodifiableSet(schedulers); + } + + public synchronized Set getDeviceNatures() { + return Collections.unmodifiableSet(deviceNatures); + } + + public Optional getChannel(String thingId, String channelId) { + Thing thing = thingIds.get(thingId); + if (thing == null) { + return Optional.empty(); + } + Map channels = thingChannels.row(thing); + Channel channel = channels.get(channelId); + return Optional.ofNullable(channel); + } + + public Optional getChannel(ChannelAddress channelAddress) { + return this.getChannel(channelAddress.getThingId(), channelAddress.getChannelId()); + } + + public Optional getChannelByAddress(String address) { + try { + return getChannel(ChannelAddress.fromString(address)); + } catch (io.openems.common.exceptions.OpenemsException e) { + return Optional.empty(); + } + } + + public Controller createController(JsonObject jController) throws ReflectionException { + String controllerClass = JsonUtils.getAsString(jController, "class"); + Controller controller; + if (jController.has("id")) { + String id = JsonUtils.getAsString(jController, "id"); + controller = (Controller) InjectionUtils.getThingInstance(controllerClass, id); + } else { + controller = (Controller) InjectionUtils.getThingInstance(controllerClass); + } + log.info("Add Controller[" + controller.id() + "], Implementation[" + controller.getClass().getSimpleName() + + "]"); + this.addThing(controller); + ConfigUtils.injectConfigChannels(this.getConfigChannels(controller), jController); + return controller; + } + + public Device createDevice(JsonObject jDevice, Bridge parent) throws ReflectionException { + String deviceClass = JsonUtils.getAsString(jDevice, "class"); + Device device = (Device) InjectionUtils.getThingInstance(deviceClass, parent); + log.info("Add Device[" + device.id() + "], Implementation[" + device.getClass().getSimpleName() + "]"); + this.addThing(device); + // instantiate DeviceNatures with Device reference + ConfigUtils.injectConfigChannels(this.getConfigChannels(device), jDevice, device); + return device; + } + + @Override + public void thingChannelsUpdated(Thing thing) { + // remove Channels from thingChannels, thingWriteChannels + Databus databus = Databus.getInstance(); + Set> thingRow = thingChannels.row(thing).entrySet(); + Iterator> i = thingRow.iterator(); + while (i.hasNext()) { + Entry thingChannel = i.next(); + if (!(thingChannel.getValue() instanceof ConfigChannel)) { + thingChannel.getValue().removeChangeListener(databus); + thingChannel.getValue().removeUpdateListener(databus); + i.remove(); + } + } + thingWriteChannels.removeAll(thing); + + // Add Channels to thingChannels, thingConfigChannels and thingWriteChannels + ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass()); + for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) { + Member member = channelDoc.getMember(); + try { + List channels = new ArrayList<>(); + boolean ignoreEmpty = false; + if (member instanceof Method) { + if (((Method) member).getReturnType().isArray()) { + ignoreEmpty = true; // ignore e.g. if getFaultChannels is returning an empty array + Channel[] ch = (Channel[]) ((Method) member).invoke(thing); + for (Channel c : ch) { + channels.add(c); + } + } else { + // It's a Method with ReturnType Channel + channels.add((Channel) ((Method) member).invoke(thing)); + } + } else if (member instanceof Field) { + // It's a Field with Type Channel + channels.add((Channel) ((Field) member).get(thing)); + } else { + continue; + } + if (!ignoreEmpty && channels.isEmpty()) { + log.warn( + "Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]"); + continue; + } + for (Channel channel : channels) { + if (channel != null) { + // Add Channel to thingChannels + thingChannels.put(thing, channel.id(), channel); + + // Add Channel to writeChannels + if (channel instanceof WriteChannel) { + thingWriteChannels.put(thing, (WriteChannel) channel); + } + + // Register Databus as listener + if (channel instanceof ReadChannel) { + ((ReadChannel) channel).addUpdateListener(databus); + ((ReadChannel) channel).addChangeListener(databus); + } + } + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + log.warn("Unable to add Channel. Member [" + member.getName() + "]", e); + } + } + } + + /** + * Gets the channel behind Thing member + * + * @param thing + * @param member + * @return + * @throws OpenemsException + */ + private Channel getChannel(Thing thing, Member member) throws OpenemsException { + Object channelObj; + if (member instanceof Field) { + Field f = (Field) member; + try { + channelObj = f.get(thing); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new OpenemsException( + "Unable to get Channel. Thing [" + thing.id() + "] Field [" + f.getName() + "]"); + } + } else { + Method m = (Method) member; + try { + channelObj = m.invoke(thing, new Object[0]); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new OpenemsException( + "Unable to get Channel. Thing [" + thing.id() + "] Method [" + m.getName() + "]"); + } + } + if (channelObj == null) { + throw new OpenemsException("Channel is null. Thing [" + thing.id() + "] Member [" + member.getName() + "]"); + } + if (!(channelObj instanceof Channel)) { + throw new OpenemsException("This is not a channel. Thing [" + thing.id() + "] Member [" + member.getName() + + "] Channel [" + channelObj + "]"); + } + return (Channel) channelObj; + } } \ No newline at end of file diff --git a/edge/src/io/openems/impl/device/commercial/WarningEss.java b/edge/src/io/openems/impl/device/commercial/WarningEss.java index 6e2d256a714..9ea9d403b9b 100644 --- a/edge/src/io/openems/impl/device/commercial/WarningEss.java +++ b/edge/src/io/openems/impl/device/commercial/WarningEss.java @@ -3,18 +3,55 @@ import io.openems.api.channel.thingstate.WarningEnum; public enum WarningEss implements WarningEnum { - EmergencyStop(0), KeyManualStop(1), TransformerPhaseBTemperatureSensorInvalidation(2), SDMemoryCardInvalidation(4), - InverterCommunicationAbnormity(5), BatteryStackCommunicationAbnormity(6), MultifunctionalAmmeterCommunicationAbnormity(7), - RemoteCommunicationAbnormity(8), PVDC1CommunicationAbnormity(9), PVDC2CommunicationAbnormity(10), TransformerSevereOvertemperature(11), - DCPrechargeContactorInspectionAbnormity(12), DCBreaker1InspectionAbnormity(13), DCBreaker2InspectionAbnormity(14), ACPrechargeContactorInspectionAbnormity(15), - ACMainontactorInspectionAbnormity(16), ACBreakerInspectionAbnormity(17), DCBreaker1CloseUnsuccessfully(18), DCBreaker2CloseUnsuccessfully(19), - ControlSignalCloseAbnormallyInspectedBySystem(20), ControlSignalOpenAbnormallyInspectedBySystem(21), NeutralWireContactorCloseUnsuccessfully(22), - NeutralWireContactorOpenUnsuccessfully(23), WorkDoorOpen(24), Emergency1Stop(25), ACBreakerCloseUnsuccessfully(26), ControlSwitchStop(27), GeneralOverload(28), - SevereOverload(29), BatteryCurrentOverLimit(30), PowerDecreaseCausedByOvertemperature(31), InverterGeneralOvertemperature(32), ACThreePhaseCurrentUnbalance(33), - RestoreFactorySettingUnsuccessfully(34), PoleBoardInvalidation(35), SelfInspectionFailed(36), ReceiveBMSFaultAndStop(37), RefrigerationEquipmentinvalidation(38), - LargeTemperatureDifferenceAmongIGBTThreePhases(39), EEPROMParametersOverRange(40), EEPROMParametersBackupFailed(41), DCBreakerCloseunsuccessfully(42), - CommunicationBetweenInverterAndBSMUDisconnected(43), CommunicationBetweenInverterAndMasterDisconnected(44), CommunicationBetweenInverterAndUCDisconnected(45), - BMSStartOvertimeControlledByPCS(46), BMSStopOvertimeControlledByPCS(47), SyncSignalInvalidation(48), SyncSignalContinuousCaputureFault(49), SyncSignalSeveralTimesCaputureFault(50); + EmergencyStop(0), // + KeyManualStop(1), // + TransformerPhaseBTemperatureSensorInvalidation(2), // + SDMemoryCardInvalidation(4), // + InverterCommunicationAbnormity(5), // + BatteryStackCommunicationAbnormity(6), // + MultifunctionalAmmeterCommunicationAbnormity(7), // + RemoteCommunicationAbnormity(8), // + PVDC1CommunicationAbnormity(9), // + PVDC2CommunicationAbnormity(10), // + TransformerSevereOvertemperature(11), // + DCPrechargeContactorInspectionAbnormity(12), // + DCBreaker1InspectionAbnormity(13), // + DCBreaker2InspectionAbnormity(14), // + ACPrechargeContactorInspectionAbnormity(15), // + ACMainontactorInspectionAbnormity(16), // + ACBreakerInspectionAbnormity(17), // + DCBreaker1CloseUnsuccessfully(18), // + DCBreaker2CloseUnsuccessfully(19), // + ControlSignalCloseAbnormallyInspectedBySystem(20), // + ControlSignalOpenAbnormallyInspectedBySystem(21), // + NeutralWireContactorCloseUnsuccessfully(22), // + NeutralWireContactorOpenUnsuccessfully(23), // + WorkDoorOpen(24), // + Emergency1Stop(25), // + ACBreakerCloseUnsuccessfully(26), // + ControlSwitchStop(27), // + GeneralOverload(28), SevereOverload(29), // + BatteryCurrentOverLimit(30), // + PowerDecreaseCausedByOvertemperature(31), // + InverterGeneralOvertemperature(32), // + ACThreePhaseCurrentUnbalance(33), // + RestoreFactorySettingUnsuccessfully(34), // + PoleBoardInvalidation(35), // + SelfInspectionFailed(36), // + ReceiveBMSFaultAndStop(37), // + RefrigerationEquipmentinvalidation(38), // + LargeTemperatureDifferenceAmongIGBTThreePhases(39), // + EEPROMParametersOverRange(40), // + EEPROMParametersBackupFailed(41), // + DCBreakerCloseunsuccessfully(42), // + CommunicationBetweenInverterAndBSMUDisconnected(43), // + CommunicationBetweenInverterAndMasterDisconnected(44), // + CommunicationBetweenInverterAndUCDisconnected(45), // + BMSStartOvertimeControlledByPCS(46), // + BMSStopOvertimeControlledByPCS(47), // + SyncSignalInvalidation(48), // + SyncSignalContinuousCaputureFault(49), // + SyncSignalSeveralTimesCaputureFault(50); public final int value; From 74eabbe51d6f4af0109db7a384a4d72d71054c2a Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 8 Feb 2018 15:35:56 +0100 Subject: [PATCH 031/156] Start architecture for EdgeWebsocket --- .../BackendApp.bndrun | 24 +- io.openems.backend.application/bnd.bnd | 4 +- .../backend/application/BackendApp.java | 42 +++- io.openems.backend.common/bnd.bnd | 4 +- .../common/events/BackendEventConstants.java | 17 ++ .../backend/common/events/package-info.java | 2 + .../bnd.bnd | 8 +- .../provider/AbstractWebsocketServer.java | 209 ++++++++++++++++++ .../impl/provider/EdgeWebsocket.java | 42 +++- .../impl/provider/EdgeWebsocketServer.java | 140 ++++++++++++ io.openems.backend.metadata.api/bnd.bnd | 1 + .../openems/backend/metadata/api/Device.java | 24 ++ .../backend/metadata/api/MetadataService.java | 12 +- .../openems/backend/metadata/api}/Role.java | 8 +- .../io/openems/backend/metadata/api/User.java | 21 +- .../backend/metadata/api/UserDevicesInfo.java | 44 ++-- .../io.openems.backend.metadata.odoo.bndrun | 13 +- .../openems/backend/metadata/odoo/Odoo.java | 93 ++++---- .../bnd.bnd | 3 +- .../provider/AbstractWebsocketServer.java | 93 +++++--- .../impl/provider/UiWebsocket.java | 29 ++- .../impl/provider/UiWebsocketServer.java | 88 ++++---- .../io/openems/common/types/DeviceImpl.java | 16 +- .../common/websocket/DefaultMessages.java | 2 +- io.openems.wrapper/websocket.bnd | 2 +- 25 files changed, 744 insertions(+), 197 deletions(-) create mode 100644 io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java create mode 100644 io.openems.backend.common/src/io/openems/backend/common/events/package-info.java create mode 100644 io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java create mode 100644 io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java rename {io.openems.common/src/io/openems/common/session => io.openems.backend.metadata.api/src/io/openems/backend/metadata/api}/Role.java (69%) diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 3d07a17c077..3e391ea14be 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -18,25 +18,21 @@ JPM-Command: openems-backend #-runproperties: felix.cm.dir=/etc/openems.d -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' -runee: JavaSE-1.8 --resolve: auto + -runbundles: \ - com.google.gson;version='[2.8.2,2.8.3)',\ - com.google.guava;version='[19.0.0,19.0.1)',\ io.openems.backend.application;version=snapshot,\ - io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ - io.openems.backend.metadata.odoo.provider;version=snapshot,\ - io.openems.backend.timedata.influx.provider;version=snapshot,\ - io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ - io.openems.common;version=snapshot,\ - io.openems.wrapper.websocket;version=snapshot,\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ org.apache.felix.scr;version='[2.0.2,2.0.3)',\ - org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ - org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ - org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ - org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.ops4j.pax.logging.pax-logging-api;version='[1.8.3,1.8.4)',\ org.ops4j.pax.logging.pax-logging-service;version='[1.8.3,1.8.4)',\ - org.osgi.service.metatype;version='[1.3.0,1.3.1)' \ No newline at end of file + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + io.openems.wrapper.websocket;version=snapshot,\ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.common;version=snapshot,\ + io.openems.backend.metadata.odoo.provider;version=snapshot,\ + io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.osgi.service.event;version='[1.3.1,1.3.2)' \ No newline at end of file diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index 079913a0d0a..cc2e7a0b852 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -16,10 +16,12 @@ Private-Package: \ io.openems.common;version=latest,\ io.openems.backend.timedata.api;version=latest,\ io.openems.backend.edgewebsocket.api;version=latest,\ - io.openems.backend.uiwebsocket.api;version=latest + io.openems.backend.uiwebsocket.api;version=latest,\ + io.openems.wrapper.websocket;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 Bundle-Name: OpenEMS Backend +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 54e9daccb4d..5e35b483758 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -14,8 +14,6 @@ import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.timedata.api.TimedataService; -import io.openems.backend.uiwebsocket.api.UiWebsocketService; @Component() public class BackendApp { @@ -27,21 +25,42 @@ public class BackendApp { @Reference MetadataService metadataService; - - @Reference - TimedataService timedataService; - + + // @Reference + // TimedataService timedataService; + @Reference EdgeWebsocketService edgeWebsocketService; - - @Reference - UiWebsocketService uiWebsocketService; - + + // @Reference + // UiWebsocketService uiWebsocketService; + + // @Reference(target = "(component.factory=EdgeWebsocketFactory)") + // private ComponentFactory factory; + + // @Reference(cardinality = ReferenceCardinality.MULTIPLE, bind = "bind", unbind + // = "unbind", policy = ReferencePolicy.DYNAMIC) + // List list; + // + // protected void bind(OneShot filter) { + // if (list == null) { + // list = new ArrayList(); + // } + // list.add(filter); + // System.out.println("add " + filter); + // } + // + // protected void unbind(OneShot filter) { + // list.remove(filter); + // System.out.println("remove " + filter); + // } + @Activate void activate() { configureLogging(); log.debug("Activate BackendApp"); + } private void configureLogging() { @@ -52,7 +71,8 @@ private void configureLogging() { log4jProps.put("log4j.rootLogger", "DEBUG, CONSOLE"); log4jProps.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); log4jProps.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); - log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); + log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", + "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); // set minimum log levels for some verbose packages log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); log4jProps.put("log4j.logger.org.apache.felix.configadmin", "INFO"); diff --git a/io.openems.backend.common/bnd.bnd b/io.openems.backend.common/bnd.bnd index 5c2603bddaf..c0d50d5cf40 100644 --- a/io.openems.backend.common/bnd.bnd +++ b/io.openems.backend.common/bnd.bnd @@ -4,7 +4,9 @@ Bundle-Version: 1.0.0.${tstamp} -Export-Package: io.openems.backend.common.session +Export-Package: \ + io.openems.backend.common.session,\ + io.openems.backend.common.events -includeresource: {readme.md} diff --git a/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java b/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java new file mode 100644 index 00000000000..b1746afe2d6 --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java @@ -0,0 +1,17 @@ +package io.openems.backend.common.events; + +public final class BackendEventConstants { + + private BackendEventConstants() { + // avoid inheritance + } + + public static final String TOPIC_BASE = "io/openems/backend/"; + + public static final String TOPIC_EDGE = "io/openems/backend/edge/"; + + public static final String TOPIC_EDGE_ONLINE = TOPIC_EDGE + "ONLINE"; + + public static final String PROPERTY_KEY_EDGE_ID = "edgeId"; + +} diff --git a/io.openems.backend.common/src/io/openems/backend/common/events/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/events/package-info.java new file mode 100644 index 00000000000..efff016e72c --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/events/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.common.events; diff --git a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd index 66075229ef1..1cb54141b2d 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd @@ -11,7 +11,13 @@ Export-Package: io.openems.backend.edgewebsocket.api -buildpath: \ osgi.enroute.base.api;version=2.1,\ - io.openems.backend.edgewebsocket.api;version=latest + io.openems.backend.edgewebsocket.api;version=latest,\ + io.openems.wrapper.websocket;version=latest,\ + com.google.gson,\ + com.google.guava,\ + io.openems.common;version=latest,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.backend.common;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java new file mode 100644 index 00000000000..66dc78d8336 --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java @@ -0,0 +1,209 @@ +package io.openems.backend.edgewebsocket.impl.provider; + +import java.net.InetSocketAddress; +import java.util.Iterator; +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.exceptions.WebsocketNotConnectedException; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; + +public abstract class AbstractWebsocketServer extends WebSocketServer { + private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); + + protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, + Optional deviceNameOpt); + + protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); + + protected abstract void _onError(WebSocket websocket, Exception ex); + + protected abstract void _onClose(WebSocket websocket); + + public AbstractWebsocketServer(int port) { + super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); + } + + @Override + public final void onStart() { + // nothing to do + } + + /** + * Open event of websocket. + */ + @Override + public final void onOpen(WebSocket websocket, ClientHandshake handshake) { + try { + this._onOpen(websocket, handshake); + } catch (Throwable e) { + log.error("onOpen-Error [" + this.handshakeToJsonObject(handshake) + "]: "); + e.printStackTrace(); + } + } + + /** + * Message event of websocket. Handles a new message. + */ + @Override + public final void onMessage(WebSocket websocket, String message) { + try { + JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); + Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); + Optional deviceNameOpt = JsonUtils.getAsOptionalString(jMessage, "device"); + this._onMessage(websocket, jMessage, jMessageIdOpt, deviceNameOpt); + } catch (Throwable e) { + log.error("onMessage-Error [" + message + "]: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Close event of websocket. Removes the websocket. Keeps the session. Calls + * _onClose() + */ + @Override + public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { + // try { + // Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); + // String sessionString; + // if (sessionOpt.isPresent()) { + // sessionString = sessionOpt.get().toString() + " "; + // } else { + // sessionString = ""; + // } + // log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + + // reason + "]"); + // this.websockets.remove(websocket); + this._onClose(websocket); + // } catch (Throwable e) { + // log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + + // e.getMessage()); + // } + } + + /** + * Error event of websocket. Logs the error. + */ + @Override + public final void onError(WebSocket websocket, Exception ex) { + // S session = this.websockets.get(websocket); + // String sessionString; + // if (session == null) { + // sessionString = ""; + // } else { + // sessionString = session.toString(); + // } + // log.warn(sessionString + " Websocket error: " + ex.getMessage()); + this._onError(websocket, ex); + } + + /** + * Get cookie from handshake + * + * @param handshake + * @return cookie as JsonObject + */ + protected Optional getSessionIdFromHandshake(ClientHandshake handshake) { + JsonObject jCookie = new JsonObject(); + if (handshake.hasFieldValue("cookie")) { + String cookieString = handshake.getFieldValue("cookie"); + for (String cookieVariable : cookieString.split("; ")) { + String[] keyValue = cookieVariable.split("="); + if (keyValue.length == 2) { + jCookie.addProperty(keyValue[0], keyValue[1]); + } + } + } + return JsonUtils.getAsOptionalString(jCookie, "session_id"); + } + + /** + * Converts a Handshake to a JsonObject + * + * @param handshake + * @return + */ + protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { + JsonObject j = new JsonObject(); + for (Iterator iter = handshake.iterateHttpFields(); iter.hasNext();) { + String field = iter.next(); + j.addProperty(field, handshake.getFieldValue(field)); + } + return j; + } + + /** + * Returns the Websocket for the given token + * + * @param name + * @return + */ + // public Optional getWebsocketByToken(String token) { + // Optional sessionOpt = (Optional) + // this.sessionManager.getSessionByToken(token); + // if (!sessionOpt.isPresent()) { + // return Optional.empty(); + // } + // S session = sessionOpt.get(); + // return Optional.ofNullable(this.websockets.inverse().get(session)); + // } + // + // protected void addWebsocket(WebSocket websocket, S session) { + // synchronized (this.websockets) { + // if (this.websockets.containsKey(websocket)) { + // log.warn("Websocket [" + websocket + "] already existed. Replacing existing + // one with session [" + // + session + "]"); + // } + // if (this.websockets.inverse().containsKey(session)) { + // log.warn("Session [" + session + "] already existed. Replacing existing one + // with websocket [" + // + websocket + "]"); + // } + // this.websockets.forcePut(websocket, session); + // } + // } + // + // protected void removeWebsocket(WebSocket websocket) { + // synchronized (this.websockets) { + // this.websockets.remove(websocket); + // } + // } + // + // protected Optional getSessionFromWebsocket(WebSocket websocket) { + // return Optional.ofNullable(this.websockets.get(websocket)); + // } + // + // protected Optional getWebsocketFromSession(S session) { + // return Optional.ofNullable(this.websockets.inverse().get(session)); + // } + /** + * Send a message to a websocket + * + * @param j + * @return true if successful, otherwise false + */ + public boolean send(WebSocket websocket, JsonObject j) { + // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); + try { + websocket.send(j.toString()); + return true; + } catch (WebsocketNotConnectedException e) { + log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; + } + } +} diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 49d54236078..b9fdf55499e 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -1,22 +1,35 @@ package io.openems.backend.edgewebsocket.impl.provider; +import java.io.IOException; + import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.event.EventAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; +import io.openems.backend.metadata.api.MetadataService; import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @Designate(ocd = EdgeWebsocket.Config.class, factory = false) -@Component(name = "EdgeWebsocket") +@Component(name = "EdgeWebsocket", immediate = true) public class EdgeWebsocket implements EdgeWebsocketService { private final Logger log = LoggerFactory.getLogger(EdgeWebsocket.class); + private EdgeWebsocketServer server = null; + + @Reference + protected MetadataService metadataService; + + @Reference + protected EventAdmin eventAdmin; + @ObjectClassDefinition @interface Config { int port(); @@ -25,11 +38,38 @@ public class EdgeWebsocket implements EdgeWebsocketService { @Activate void activate(Config config) { log.debug("Activate EdgeWebsocket [port=" + config.port() + "]"); + + this.stopServer(); + this.startServer(config.port()); } @Deactivate void deactivate() { log.debug("Deactivate EdgeWebsocket"); + this.stopServer(); + } + + /** + * Stop existing websocket server + */ + private synchronized void stopServer() { + if (this.server != null) { + try { + this.server.stop(); + } catch (IOException | InterruptedException e) { + log.error("Unable to stop existing EdgeWebsocketServer: " + e.getMessage()); + } + } + } + + /** + * Create and start new server + * + * @param port + */ + private synchronized void startServer(int port) { + this.server = new EdgeWebsocketServer(this, port); + this.server.start(); } } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java new file mode 100644 index 00000000000..62b3b291749 --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -0,0 +1,140 @@ +package io.openems.backend.edgewebsocket.impl.provider; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.java_websocket.WebSocket; +import org.java_websocket.framing.CloseFrame; +import org.java_websocket.handshake.ClientHandshake; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.openems.backend.common.events.BackendEventConstants; +import io.openems.backend.metadata.api.Device; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.WebSocketUtils; + +public class EdgeWebsocketServer extends AbstractWebsocketServer { + + private final EdgeWebsocket parent; + private final Map websocketsMap = new HashMap<>(); + private final Logger log = LoggerFactory.getLogger(EdgeWebsocketServer.class); + + public EdgeWebsocketServer(EdgeWebsocket parent, int port) { + super(port); + this.parent = parent; + } + + @Override + protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + String apikey = ""; + try { + // get apikey from handshake + Optional apikeyOpt = parseApikeyFromHandshake(handshake); + if (!apikeyOpt.isPresent()) { + throw new OpenemsException("Apikey is missing in handshake"); + } + apikey = apikeyOpt.get(); + + // get edgeId for apikey + int[] edgeIds = this.parent.metadataService.getEdgeIdsForApikey(apikey); + + // if existing: close existing websocket for this apikey + synchronized (this.websocketsMap) { + for (int edgeId : edgeIds) { + if (this.websocketsMap.containsKey(edgeId)) { + WebSocket oldWebsocket = this.websocketsMap.get(edgeId); + oldWebsocket.closeConnection(CloseFrame.REFUSE, + "Another device with this apikey [" + apikey + "] connected."); + } + // add websocket to local cache + this.websocketsMap.put(edgeId, websocket); + } + } + + // send successful reply to openems + JsonObject jReply = DefaultMessages.openemsConnectionSuccessfulReply(); + WebSocketUtils.send(websocket, jReply); + + // announce device as online + for (int edgeId : edgeIds) { + Map properties = new HashMap<>(); + properties.put(BackendEventConstants.PROPERTY_KEY_EDGE_ID, edgeId); + Event event = new Event(BackendEventConstants.TOPIC_EDGE_ONLINE, properties); + this.parent.eventAdmin.postEvent(event); + } + + // log + for (int edgeId : edgeIds) { + Optional deviceOpt = this.parent.metadataService.getDevice(edgeId); + if (deviceOpt.isPresent()) { + log.info("Device [" + deviceOpt.get() + "] connected."); + } else { + log.info("Device [ID:" + edgeId + "] connected."); + } + } + + // TODO do this in Metadata + // try { + // // set device active (in Odoo) + // for (MetadataDevice device : devices) { + // if (device.getState().equals("inactive")) { + // device.setState("active"); + // } + // device.setLastMessage(); + // device.writeObject(); + // } + // } catch (OpenemsException e) { + // // this error does not stop the connection + // log.error("Device [" + String.join(",", deviceNames) + "] error: " + + // e.getMessage()); + // } + + } catch (OpenemsException e) { + // send connection failed to OpenEMS + JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); + WebSocketUtils.send(websocket, jReply); + // close websocket + websocket.closeConnection(CloseFrame.REFUSE, "OpenEMS connection failed. Apikey [" + apikey + "]"); + } + } + + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, + Optional deviceNameOpt) { + System.out.println("_onMessage"); + } + + @Override + protected void _onError(WebSocket websocket, Exception ex) { + System.out.println("_onError"); + } + + @Override + protected void _onClose(WebSocket websocket) { + System.out.println("_onClose"); + } + + /** + * Parses the apikey from websocket onOpen handshake + * + * @param handshake + * @return + */ + private Optional parseApikeyFromHandshake(ClientHandshake handshake) { + if (handshake.hasFieldValue("apikey")) { + String apikey = handshake.getFieldValue("apikey"); + return Optional.ofNullable(apikey); + } + return Optional.empty(); + } +} diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd index 64db203fafd..1079424577d 100644 --- a/io.openems.backend.metadata.api/bnd.bnd +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -24,3 +24,4 @@ Require-Capability: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java new file mode 100644 index 00000000000..e9519cceb8f --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java @@ -0,0 +1,24 @@ +package io.openems.backend.metadata.api; + +public class Device { + private final int id; + private String name; + private String comment; + private String producttype; + + public Device(int id, String name, String comment, String producttype) { + this.id = id; + this.name = name; + this.comment = comment; + this.producttype = producttype; + } + + public int getId() { + return id; + } + + @Override + public String toString() { + return "Device [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype + "]"; + } +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index c7a092326e0..528269d7eda 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -1,5 +1,7 @@ package io.openems.backend.metadata.api; +import java.util.Optional; + import org.osgi.annotation.versioning.ProviderType; import io.openems.common.exceptions.OpenemsException; @@ -7,8 +9,10 @@ @ProviderType public interface MetadataService { - public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsException; - - public OLD_MetadataDeviceModel getDeviceModel(); - + public abstract User getUserWithSession(String sessionId) throws OpenemsException; + + public abstract int[] getEdgeIdsForApikey(String apikey); + + public abstract Optional getDevice(int edgeId); + } diff --git a/io.openems.common/src/io/openems/common/session/Role.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Role.java similarity index 69% rename from io.openems.common/src/io/openems/common/session/Role.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Role.java index 3d6c44e55ca..8617b49bf83 100644 --- a/io.openems.common/src/io/openems/common/session/Role.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Role.java @@ -1,8 +1,14 @@ -package io.openems.common.session; +package io.openems.backend.metadata.api; public enum Role { ADMIN, INSTALLER, OWNER, GUEST; + /** + * Returns the Role ENUM for this name or "GUEST" if it was not found. + * + * @param name + * @return + */ public static Role getRole(String name) { switch (name.toLowerCase()) { case "admin": diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java index 2dc2b93ded7..2565dde154c 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java @@ -1,11 +1,17 @@ package io.openems.backend.metadata.api; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public class User { private final int id; private String name; + private final Map deviceRoles = new HashMap<>(); - public User(int id) { + public User(int id, String name) { this.id = id; + this.name = name; } public String getName() { @@ -15,4 +21,17 @@ public String getName() { public int getId() { return id; } + + public void addDeviceRole(int deviceId, Role role) { + this.deviceRoles.put(deviceId, role); + } + + public Map getDeviceRoles() { + return Collections.unmodifiableMap(this.deviceRoles); + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + ", deviceRole=" + deviceRoles + "]"; + } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java index 6318dd454e8..58e384fd55b 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java @@ -1,36 +1,32 @@ package io.openems.backend.metadata.api; -import com.google.common.collect.Multimap; - -import io.openems.common.types.DeviceImpl; +import java.util.HashMap; +import java.util.Map; public class UserDevicesInfo { - private int userId; - private String userName; - private Multimap deviceMap; - - public void setUserId(int userId) { - this.userId = userId; - } - - public void setUserName(String userName) { - this.userName = userName; - } + private final User user; + private final Map devices = new HashMap<>(); - public void setDevices(Multimap deviceMap) { - this.deviceMap = deviceMap; + public UserDevicesInfo(User user) { + super(); + this.user = user; } - - public int getUserId() { - return userId; + + public void addDevice(Device device) { + this.devices.put(device.getId(), device); } - public String getUserName() { - return userName; + public User getUser() { + return user; } - public Multimap getDeviceMap() { - return deviceMap; - } + public Map getDevices() { + return devices; + } + + @Override + public String toString() { + return "UserDevicesInfo [user=" + user + ", devices=" + devices + "]"; + } } diff --git a/io.openems.backend.metadata.odoo.provider/io.openems.backend.metadata.odoo.bndrun b/io.openems.backend.metadata.odoo.provider/io.openems.backend.metadata.odoo.bndrun index a3f971763ee..3cc4e5ca576 100644 --- a/io.openems.backend.metadata.odoo.provider/io.openems.backend.metadata.odoo.bndrun +++ b/io.openems.backend.metadata.odoo.provider/io.openems.backend.metadata.odoo.bndrun @@ -10,4 +10,15 @@ JPM-Command: provider -runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo.provider)' --runbundles: ${error;You must first resolve this bndrun file before you can run it} +-runbundles: \ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.backend.metadata.odoo.provider;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + slf4j.api;version='[1.8.0,1.8.1)' diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index df7fa53d4cf..466a52d33bf 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -1,21 +1,5 @@ package io.openems.backend.metadata.odoo; -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.metatype.annotations.Designate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.backend.metadata.api.OLD_MetadataDeviceModel; -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.metadata.api.UserDevicesInfo; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.DeviceImpl; -import io.openems.common.utils.JsonUtils; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -24,13 +8,29 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; +import java.util.Optional; + +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.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.common.collect.LinkedHashMultimap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import io.openems.backend.metadata.api.Device; +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.metadata.api.Role; +import io.openems.backend.metadata.api.User; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; + @Designate(ocd = Odoo.Config.class, factory = false) @Component(name = "Odoo", configurationPolicy = ConfigurationPolicy.REQUIRE) public class Odoo implements MetadataService { @@ -68,13 +68,6 @@ void deactivate() { log.debug("Deactivate Odoo"); } - private OLD_MetadataDeviceModel deviceModel; - - @Override - public OLD_MetadataDeviceModel getDeviceModel() { - return deviceModel; - } - /** * Tries to authenticate at the Odoo server using a sessionId from a cookie. * Updates the Session object accordingly. @@ -84,7 +77,7 @@ public OLD_MetadataDeviceModel getDeviceModel() { * @throws OpenemsException */ @Override - public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsException { + public User getUserWithSession(String sessionId) throws OpenemsException { HttpURLConnection connection = null; try { // send request to Odoo @@ -115,25 +108,24 @@ public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsExcept } if (j.has("result")) { - UserDevicesInfo info = new UserDevicesInfo(); - // parse the result JsonObject jResult = JsonUtils.getAsJsonObject(j, "result"); JsonObject jUser = JsonUtils.getAsJsonObject(jResult, "user"); - info.setUserId(JsonUtils.getAsInt(jUser, "id")); - info.setUserName(JsonUtils.getAsString(jUser, "name")); + User user = new User(// + JsonUtils.getAsInt(jUser, "id"), // + JsonUtils.getAsString(jUser, "name")); JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); - LinkedHashMultimap deviceMap = LinkedHashMultimap.create(); for (JsonElement jDevice : jDevices) { - String name = JsonUtils.getAsString(jDevice, "name"); - deviceMap.put(name, new DeviceImpl( // - name, // + Device device = new Device(// + JsonUtils.getAsInt(jDevice, "id"), // + JsonUtils.getAsString(jDevice, "name"), // JsonUtils.getAsString(jDevice, "comment"), // - JsonUtils.getAsString(jDevice, "producttype"), // - JsonUtils.getAsString(jDevice, "role"))); + JsonUtils.getAsString(jDevice, "producttype")); + // this.devices.putIfAbsent(device.getId(), device); + user.addDeviceRole(device.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); } - info.setDevices(deviceMap); - return info; + // this.users.put(user.getId(), user); + return user; } } } catch (IOException e) { @@ -145,4 +137,31 @@ public UserDevicesInfo getInfoWithSession(String sessionId) throws OpenemsExcept } throw new OpenemsException("No result from Odoo"); } + + @Override + public int[] getEdgeIdsForApikey(String apikey) { + // TODO Auto-generated method stub + return new int[] { -1 }; + } + + @Override + public Optional getDevice(int edgeId) { + // TODO Auto-generated method stub + return Optional.empty(); + } + + // public Optional getUser(int id) { + // return Optional.ofNullable(this.users.get(id)); + // } + // + // public Optional getDevice(int id) { + // return Optional.ofNullable(this.devices.get(id)); + // } + // + // // private + // protected final ConcurrentMap devices = new + // ConcurrentHashMap<>(); + // protected final ConcurrentMap users = new + // ConcurrentHashMap<>(); + } diff --git a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd index 09fd56d71d6..f8444e0928a 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd @@ -17,7 +17,8 @@ Private-Package: io.openems.backend.uiwebsocket.impl.provider com.google.gson,\ io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ - io.openems.backend.uiwebsocket.api;version=latest + io.openems.backend.uiwebsocket.api;version=latest,\ + io.openems.backend.edgewebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java index c04dce1bad4..d3aa2379f0f 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java @@ -6,6 +6,7 @@ import org.java_websocket.WebSocket; import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; import org.slf4j.Logger; @@ -17,6 +18,7 @@ import com.google.gson.JsonParser; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; public abstract class AbstractWebsocketServer extends WebSocketServer { private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); @@ -149,40 +151,59 @@ protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { * @param name * @return */ -// public Optional getWebsocketByToken(String token) { -// Optional sessionOpt = (Optional) this.sessionManager.getSessionByToken(token); -// if (!sessionOpt.isPresent()) { -// return Optional.empty(); -// } -// S session = sessionOpt.get(); -// return Optional.ofNullable(this.websockets.inverse().get(session)); -// } -// -// protected void addWebsocket(WebSocket websocket, S session) { -// synchronized (this.websockets) { -// if (this.websockets.containsKey(websocket)) { -// log.warn("Websocket [" + websocket + "] already existed. Replacing existing one with session [" -// + session + "]"); -// } -// if (this.websockets.inverse().containsKey(session)) { -// log.warn("Session [" + session + "] already existed. Replacing existing one with websocket [" -// + websocket + "]"); -// } -// this.websockets.forcePut(websocket, session); -// } -// } -// -// protected void removeWebsocket(WebSocket websocket) { -// synchronized (this.websockets) { -// this.websockets.remove(websocket); -// } -// } -// -// protected Optional getSessionFromWebsocket(WebSocket websocket) { -// return Optional.ofNullable(this.websockets.get(websocket)); -// } -// -// protected Optional getWebsocketFromSession(S session) { -// return Optional.ofNullable(this.websockets.inverse().get(session)); -// } + // public Optional getWebsocketByToken(String token) { + // Optional sessionOpt = (Optional) + // this.sessionManager.getSessionByToken(token); + // if (!sessionOpt.isPresent()) { + // return Optional.empty(); + // } + // S session = sessionOpt.get(); + // return Optional.ofNullable(this.websockets.inverse().get(session)); + // } + // + // protected void addWebsocket(WebSocket websocket, S session) { + // synchronized (this.websockets) { + // if (this.websockets.containsKey(websocket)) { + // log.warn("Websocket [" + websocket + "] already existed. Replacing existing + // one with session [" + // + session + "]"); + // } + // if (this.websockets.inverse().containsKey(session)) { + // log.warn("Session [" + session + "] already existed. Replacing existing one + // with websocket [" + // + websocket + "]"); + // } + // this.websockets.forcePut(websocket, session); + // } + // } + // + // protected void removeWebsocket(WebSocket websocket) { + // synchronized (this.websockets) { + // this.websockets.remove(websocket); + // } + // } + // + // protected Optional getSessionFromWebsocket(WebSocket websocket) { + // return Optional.ofNullable(this.websockets.get(websocket)); + // } + // + // protected Optional getWebsocketFromSession(S session) { + // return Optional.ofNullable(this.websockets.inverse().get(session)); + // } + /** + * Send a message to a websocket + * + * @param j + * @return true if successful, otherwise false + */ + public boolean send(WebSocket websocket, JsonObject j) { + // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); + try { + websocket.send(j.toString()); + return true; + } catch (WebsocketNotConnectedException e) { + log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; + } + } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 9885e5ae775..3a3c3280f1b 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -10,36 +10,43 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; import org.osgi.service.metatype.annotations.Designate; - -@Designate( ocd=UiWebsocket.Config.class, factory=true) -@Component(name="UiWebsocket") +@Designate(ocd = UiWebsocket.Config.class, factory = false) +@Component(name = "UiWebsocket") public class UiWebsocket implements UiWebsocketService { private final Logger log = LoggerFactory.getLogger(UiWebsocket.class); - + private UiWebsocketServer server = null; - + @Reference private MetadataService metadataService; - + protected MetadataService getMetadataService() { return metadataService; } - + + @Reference + private EdgeWebsocketService edgeWebsocketService; + + protected EdgeWebsocketService getEdgeWebsocketService() { + return edgeWebsocketService; + } + @ObjectClassDefinition @interface Config { int port(); } - + @Activate void activate(Config config) { log.debug("Activate UiWebsocket [port=" + config.port() + "]"); - + this.stopServer(); this.startServer(config.port()); @@ -55,7 +62,7 @@ void deactivate() { * Stop existing websocket server */ private void stopServer() { - if(this.server != null) { + if (this.server != null) { try { this.server.stop(); } catch (IOException | InterruptedException e) { @@ -63,7 +70,7 @@ private void stopServer() { } } } - + /** * Create and start new server * diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index f97d992f648..f5d0188bd13 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -1,8 +1,10 @@ package io.openems.backend.uiwebsocket.impl.provider; +import java.util.Map.Entry; import java.util.Optional; import org.java_websocket.WebSocket; +import org.java_websocket.framing.CloseFrame; import org.java_websocket.handshake.ClientHandshake; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,13 +12,13 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.metadata.api.UserDevicesInfo; +import io.openems.backend.metadata.api.Role; +import io.openems.backend.metadata.api.User; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.DefaultMessages; public class UiWebsocketServer extends AbstractWebsocketServer { - + private final Logger log = LoggerFactory.getLogger(UiWebsocketServer.class); private final UiWebsocket parent; @@ -28,52 +30,56 @@ public UiWebsocketServer(UiWebsocket parent, int port) { @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { String error = ""; - + User user = null; + // login using session_id from the cookie Optional sessionIdOpt = getSessionIdFromHandshake(handshake); - if(!sessionIdOpt.isPresent()) { + if (!sessionIdOpt.isPresent()) { error = "Session-ID is missing in handshake"; } else { - UserDevicesInfo info; try { - info = this.parent.getMetadataService().getInfoWithSession(sessionIdOpt.get()); - System.out.println(info.getUserName()); + user = this.parent.getMetadataService().getUserWithSession(sessionIdOpt.get()); } catch (OpenemsException e) { error = e.getMessage(); } } - System.out.println(error); - -// // check if the session is now valid and send reply to browser -// BrowserSessionData data = session.getData(); -// if (error.isEmpty()) { -// // add isOnline information -// OpenemsWebsocketSingleton openemsWebsocket = OpenemsWebsocket.instance(); -// for (DeviceImpl device : data.getDevices()) { -// device.setOnline(openemsWebsocket.isOpenemsWebsocketConnected(device.getName())); -// } -// -// // send connection successful to browser -// JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), Optional.empty(), -// data.getDevices()); -// // TODO write user name to log output -// WebSocketUtils.send(websocket, jReply); -// -// // add websocket to local cache -// this.addWebsocket(websocket, session); -// -// log.info("User [" + data.getUserName() + "] connected with Session [" + data.getOdooSessionId().orElse("") -// + "]."); -// -// } else { -// // send connection failed to browser -// JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); -// WebSocketUtils.send(websocket, jReply); -// log.warn("User [" + data.getUserName() + "] connection failed. Session [" -// + data.getOdooSessionId().orElse("") + "] Error [" + error + "]."); -// -// websocket.closeConnection(CloseFrame.REFUSE, error); -// } + + if (!error.isEmpty()) { + // send connection failed to browser + this.send(websocket, DefaultMessages.browserConnectionFailedReply()); + log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + error + "]."); + websocket.closeConnection(CloseFrame.REFUSE, error); + } else if (user != null) { + // send connection successful to browser + for(Entry deviceRole : user.getDeviceRoles().entrySet()) { +// boolean isOnline = this.parent.getEdgeWebsocketService().isOnline(deviceRole.getKey()); +// JsonArray jDevices + } + +// +// +// JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), +// Optional.empty(), +// data.getDevices()); +// // TODO write user name to log output +// WebSocketUtils.send(websocket, jReply); +// +// // add websocket to local cache +// this.addWebsocket(websocket, session); +// log.info("User [" + data.getUserName() + "] connected with Session [" + +// data.getOdooSessionId().orElse("") +// + "]."); + + } + + // // check if the session is now valid and send reply to browser + // BrowserSessionData data = session.getData(); + // if (error.isEmpty()) { + // // add isOnline information + // + + // + } @Override diff --git a/io.openems.common/src/io/openems/common/types/DeviceImpl.java b/io.openems.common/src/io/openems/common/types/DeviceImpl.java index 124d8e721a9..eac3644602b 100644 --- a/io.openems.common/src/io/openems/common/types/DeviceImpl.java +++ b/io.openems.common/src/io/openems/common/types/DeviceImpl.java @@ -2,8 +2,6 @@ import java.util.Optional; -import io.openems.common.session.Role; - /** * Helper class to store tuple of device name and role * @@ -14,23 +12,23 @@ public class DeviceImpl implements Comparable, Device { private final String name; private final String comment; private final String producttype; - private final Role role; +// private final Role role; private boolean online = false; public DeviceImpl(String name, String comment, String producttype, String role) { this.name = name; this.comment = comment; this.producttype = producttype; - this.role = Role.getRole(role); +// this.role = Role.getRole(role); } public String getName() { return name; } - public Role getRole() { - return role; - } +// public Role getRole() { +//// return role; +// } public void setOnline(boolean online) { this.online = online; @@ -50,8 +48,8 @@ public String getProducttype() { @Override public int compareTo(DeviceImpl other) { - return (this.name + this.comment + this.producttype + this.role.toString() + this.online) - .compareTo(other.name + other.comment + other.producttype + other.role.toString() + other.online); + return (this.name + this.comment + this.producttype + this.online) + .compareTo(other.name + other.comment + other.producttype + other.online); } @Override diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 85db6caef14..1e23d00a235 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -57,7 +57,7 @@ public static JsonObject browserConnectionSuccessfulReply(String token, Optional jDevice.addProperty("name", device.getName()); jDevice.addProperty("comment", device.getComment()); jDevice.addProperty("producttype", device.getProducttype()); - jDevice.addProperty("role", device.getRole().toString()); +// TODO jDevice.addProperty("role", device.getRole().toString()); jDevice.addProperty("online", device.isOnline()); jDevices.add(jDevice); } diff --git a/io.openems.wrapper/websocket.bnd b/io.openems.wrapper/websocket.bnd index 92068295f39..ec30bce173a 100644 --- a/io.openems.wrapper/websocket.bnd +++ b/io.openems.wrapper/websocket.bnd @@ -13,7 +13,7 @@ Bundle-Version: 1.3.7 Include-Resource: @jar/Java-WebSocket-1.3.7.jar, OSGI-OPT/src=@jar/Java-WebSocket-1.3.7-sources.jar --exportcontents: org.java_websocket, org.java_websocket.drafts, org.java_websocket.exceptions, org.java_websocket.handshake, org.java_websocket.server +-exportcontents: org.java_websocket, org.java_websocket.drafts, org.java_websocket.exceptions, org.java_websocket.handshake, org.java_websocket.server, org.java_websocket.framing -sources: false Bundle-Version: 1.3.7 \ No newline at end of file From 12984c62c786478b3dd56bc4ef40b58e5d399847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Feb 2018 10:49:22 +0100 Subject: [PATCH 032/156] implement SymmetricPower instead of setActivePower and setReactivePower --- .../openems/impl/controller/debuglog/Ess.java | 188 ++++++----- .../riedmann/RiedmannController.java | 12 +- .../impl/controller/supplybusswitch/Ess.java | 4 - .../AvoidTotalChargeController.java | 10 +- .../symmetric/avoidtotalcharge/Ess.java | 90 ++--- .../AvoidTotalDischargeController.java | 38 +-- .../symmetric/avoidtotaldischarge/Ess.java | 162 ++++----- ...idTotalDischargeSocTimeLineController.java | 315 +++++++++--------- .../avoidtotaldischargesoctimeline/Ess.java | 210 ++++++------ .../balancing/BalancingController.java | 27 +- .../controller/symmetric/balancing/Ess.java | 14 +- ...BalancingBandgapActivePowerController.java | 91 +++++ ...ancingBandgapReactivePowerController.java} | 57 +--- .../symmetric/balancingbandgap/Ess.java | 129 ++++--- .../BalancingCosPhiController.java | 19 +- .../symmetric/balancingcosphi/Ess.java | 13 +- .../BalancingCurrentController.java | 184 +++++----- .../symmetric/balancingcurrent/Ess.java | 114 +++---- .../BalancingOffsetActivePowerController.java | 81 +++++ .../BalancingOffsetController.java | 138 -------- ...alancingOffsetReactivePowerController.java | 78 +++++ .../symmetric/balancingoffset/Ess.java | 129 ++++--- .../BalancingSurplusController.java | 251 +++++++------- .../symmetric/balancingsurplus/Ess.java | 129 ++++--- .../capacitytest/CapacityTestController.java | 304 ++++++++--------- .../symmetric/capacitytest/Ess.java | 116 +++---- .../EnergysavingController.java | 116 ------- .../symmetric/commercialenergysaver/Ess.java | 45 --- .../symmetric/commercialworkstate/Ess.java | 86 +++-- .../symmetric/cosphi/CosPhiController.java | 15 +- .../impl/controller/symmetric/cosphi/Ess.java | 99 +++--- .../CosPhiCharacteristicController.java | 64 ++-- .../symmetric/cosphicharacteristic/Ess.java | 103 +++--- .../controller/symmetric/fixvalue/Ess.java | 87 ++--- .../FixValueActivePowerController.java | 72 ++++ ...a => FixValueReactivePowerController.java} | 25 +- .../symmetric/powerbyfrequency/Ess.java | 116 +++---- .../PowerByFrequencyController.java | 184 +++++----- ...a => ActivePowerLimitationController.java} | 185 +++++----- .../symmetric/powerlimitation/Ess.java | 94 +++--- .../controller/symmetric/powerramp/Ess.java | 99 +++--- .../powerramp/PowerRampController.java | 21 +- .../AvoidTotalDischargeController.java | 219 ------------ .../refuavoidtotaldischarge/Ess.java | 76 ----- .../symmetric/refuworkstate/Ess.java | 90 +++-- .../symmetric/timelinecharge/Ess.java | 106 +++--- .../TimelineChargeController.java | 272 ++++++--------- ...PowerVoltageCharacteristicController.java} | 256 +++++++------- .../symmetric/voltagecharacteristic/Ess.java | 133 ++++---- ...ePowerVoltageCharacteristicController.java | 112 +++++++ .../impl/controller/testwrite/Ess.java | 76 ++--- 51 files changed, 2673 insertions(+), 2981 deletions(-) create mode 100644 edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java rename edge/src/io/openems/impl/controller/symmetric/balancingbandgap/{BalancingBandgapController.java => BalancingBandgapReactivePowerController.java} (58%) create mode 100644 edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java delete mode 100644 edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetController.java create mode 100644 edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java delete mode 100644 edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/EnergysavingController.java delete mode 100644 edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/Ess.java create mode 100644 edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java rename edge/src/io/openems/impl/controller/symmetric/fixvalue/{FixValueController.java => FixValueReactivePowerController.java} (74%) rename edge/src/io/openems/impl/controller/symmetric/powerlimitation/{PowerLimitationController.java => ActivePowerLimitationController.java} (51%) delete mode 100644 edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java delete mode 100644 edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/Ess.java rename edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/{VoltageCharacteristicController.java => ActivePowerVoltageCharacteristicController.java} (61%) create mode 100644 edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java diff --git a/edge/src/io/openems/impl/controller/debuglog/Ess.java b/edge/src/io/openems/impl/controller/debuglog/Ess.java index 53938bc927a..77dfe0f39fd 100644 --- a/edge/src/io/openems/impl/controller/debuglog/Ess.java +++ b/edge/src/io/openems/impl/controller/debuglog/Ess.java @@ -1,89 +1,99 @@ -/******************************************************************************* - * 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.debuglog; - -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.AsymmetricEssNature; -import io.openems.api.device.nature.ess.EssNature; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = EssNature.class) -public class Ess extends ThingMap { - - private final EssNature ess; - - public Ess(EssNature ess) { - super(ess); - this.ess = ess; - - ess.allowedCharge().required(); - ess.allowedDischarge().required(); - ess.minSoc().required(); - ess.soc().required(); - ess.systemState().required(); - - if (ess instanceof AsymmetricEssNature) { - AsymmetricEssNature e = (AsymmetricEssNature) ess; - e.activePowerL1().required(); - e.activePowerL2().required(); - e.activePowerL3().required(); - e.reactivePowerL1().required(); - e.reactivePowerL2().required(); - e.reactivePowerL3().required(); - } - if (ess instanceof SymmetricEssNature) { - SymmetricEssNature e = (SymmetricEssNature) ess; - e.activePower().required(); - e.reactivePower().required(); - } - } - - @Override public String toString() { - StringBuilder b = new StringBuilder(); - b.append(ess.id() + " [" + // - "SOC:" + ess.soc().format() + "|"); - if (ess instanceof SymmetricEssNature) { - SymmetricEssNature e = (SymmetricEssNature) ess; - b.append("L:" + e.activePower().format() + ";" + e.reactivePower().format()); - } - if (ess instanceof AsymmetricEssNature && ess instanceof SymmetricEssNature) { - b.append("|"); - } - if (ess instanceof AsymmetricEssNature) { - AsymmetricEssNature e = (AsymmetricEssNature) ess; - b.append("L1:" + e.activePowerL1().format() + ";" + e.reactivePowerL1().format() + "|" + // - "L2:" + e.activePowerL2().format() + ";" + e.reactivePowerL2().format() + "|" + // - "L3:" + e.activePowerL3().format() + ";" + e.reactivePowerL3().format()); - } - b.append("|" + // - "Allowed:" + ess.allowedCharge().format() + ";" + ess.allowedDischarge().format()); - b.append("|" + // - "GridMode:" + ess.gridMode().labelOptional().orElse("unknown")); - String warn = ess.warning().toString(); - if (!warn.equals("")) { - b.append("|Warn:" + ess.warning()); - } - b.append("]"); - return b.toString(); - } - -} +/******************************************************************************* + * 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.debuglog; + +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.AsymmetricEssNature; +import io.openems.api.device.nature.ess.EssNature; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.impl.device.commercial.FeneconCommercialEss; + +@IsThingMap(type = EssNature.class) +public class Ess extends ThingMap { + + private final EssNature ess; + + public Ess(EssNature ess) { + super(ess); + this.ess = ess; + + ess.allowedCharge().required(); + ess.allowedDischarge().required(); + ess.minSoc().required(); + ess.soc().required(); + ess.systemState().required(); + + if (ess instanceof AsymmetricEssNature) { + AsymmetricEssNature e = (AsymmetricEssNature) ess; + e.activePowerL1().required(); + e.activePowerL2().required(); + e.activePowerL3().required(); + e.reactivePowerL1().required(); + e.reactivePowerL2().required(); + e.reactivePowerL3().required(); + } + if (ess instanceof SymmetricEssNature) { + SymmetricEssNature e = (SymmetricEssNature) ess; + e.activePower().required(); + e.reactivePower().required(); + } + if(ess instanceof FeneconCommercialEss) { + FeneconCommercialEss e = (FeneconCommercialEss) ess; + e.acDischargeEnergy.required(); + e.acChargeEnergy.required(); + } + } + + @Override public String toString() { + StringBuilder b = new StringBuilder(); + b.append(ess.id() + " [" + // + "SOC:" + ess.soc().format() + "|"); + if (ess instanceof SymmetricEssNature) { + SymmetricEssNature e = (SymmetricEssNature) ess; + b.append("L:" + e.activePower().format() + ";" + e.reactivePower().format()); + } + if (ess instanceof AsymmetricEssNature && ess instanceof SymmetricEssNature) { + b.append("|"); + } + if (ess instanceof AsymmetricEssNature) { + AsymmetricEssNature e = (AsymmetricEssNature) ess; + b.append("L1:" + e.activePowerL1().format() + ";" + e.reactivePowerL1().format() + "|" + // + "L2:" + e.activePowerL2().format() + ";" + e.reactivePowerL2().format() + "|" + // + "L3:" + e.activePowerL3().format() + ";" + e.reactivePowerL3().format()); + } + if(ess instanceof FeneconCommercialEss) { + FeneconCommercialEss e = (FeneconCommercialEss) ess; + b.append("Energy: "+e.acDischargeEnergy.format()+e.acChargeEnergy.format()); + } + b.append("|" + // + "Allowed:" + ess.allowedCharge().format() + ";" + ess.allowedDischarge().format()); + b.append("|" + // + "GridMode:" + ess.gridMode().labelOptional().orElse("unknown")); + String warn = ess.warning().toString(); + if (!warn.equals("")) { + b.append("|Warn:" + ess.warning()); + } + b.append("]"); + return b.toString(); + } + +} diff --git a/edge/src/io/openems/impl/controller/riedmann/RiedmannController.java b/edge/src/io/openems/impl/controller/riedmann/RiedmannController.java index bd82ddcad55..3c940563770 100644 --- a/edge/src/io/openems/impl/controller/riedmann/RiedmannController.java +++ b/edge/src/io/openems/impl/controller/riedmann/RiedmannController.java @@ -34,13 +34,13 @@ public class RiedmannController extends Controller implements ChannelChangeListe public ConfigChannel setWaterLevelBorehole3Off = new ConfigChannel("wl3Off", this).defaultValue(500L); @ChannelInfo(title = "Soc Hysteresis", description = "hysteresis for the switching of the loads.", type = Long.class) public ConfigChannel socHysteresis = new ConfigChannel("socHysteresis", this).defaultValue(10L); - @ChannelInfo(title = "Soc Load 1 Off", description = "Below this Soc the Load 1 will be disconnected.", type = Long.class) + @ChannelInfo(title = "Soc Load 1 Off", description = "Below this Soc the Load 1(Clima1&Clima2) will be disconnected.", type = Long.class) public ConfigChannel socLoad1Off = new ConfigChannel<>("socLoad1Off", this); - @ChannelInfo(title = "Soc Load 2 Off", description = "Below this Soc the Load 2 will be disconnected.", type = Long.class) + @ChannelInfo(title = "Soc Load 2 Off", description = "Below this Soc the Load 2(Pivot) will be disconnected.", type = Long.class) public ConfigChannel socLoad2Off = new ConfigChannel<>("socLoad2Off", this); - @ChannelInfo(title = "Soc Load 3 Off", description = "Below this Soc the Load 3 will be disconnected.", type = Long.class) + @ChannelInfo(title = "Soc Load 3 Off", description = "Below this Soc the Load 3(Borehole1,2&3) will be disconnected.", type = Long.class) public ConfigChannel socLoad3Off = new ConfigChannel<>("socLoad3Off", this); - @ChannelInfo(title = "Soc Load 4 Off", description = "Below this Soc the Load 4 will be disconnected.", type = Long.class) + @ChannelInfo(title = "Soc Load 4 Off", description = "Below this Soc the Load 4(office&traineeCenter) will be disconnected.", type = Long.class) public ConfigChannel socLoad4Off = new ConfigChannel<>("socLoad4Off", this); @ChannelInfo(title = "SPS", description = "The sps which should be controlled.", type = Custom.class) @@ -165,6 +165,7 @@ public void run() { log.error("TimelineChargeController error: " + e.getMessage()); return; } + //Load1 try { if (essSoc >= socLoad1Off + socHysteresis || ess.gridMode.labelOptional().equals(Optional.of(EssNature.ON_GRID))) { @@ -182,6 +183,7 @@ public void run() { } catch (WriteChannelException e) { log.error("Failed to connect/disconnect Load 1: " + e.getMessage()); } + //Load2 try { if (essSoc >= socLoad2Off + socHysteresis || ess.gridMode.labelOptional().equals(Optional.of(EssNature.ON_GRID))) { @@ -197,6 +199,7 @@ public void run() { } catch (WriteChannelException e) { log.error("Failed to connect/disconnect Load 2: " + e.getMessage()); } + //Load3 try { if (essSoc >= socLoad3Off + socHysteresis || ess.gridMode.labelOptional().equals(Optional.of(EssNature.ON_GRID))) { @@ -216,6 +219,7 @@ public void run() { } catch (WriteChannelException e) { log.error("Failed to connect/disconnect Load 3: " + e.getMessage()); } + //Load4 try { if (essSoc >= socLoad4Off + socHysteresis || ess.gridMode.labelOptional().equals(Optional.of(EssNature.ON_GRID))) { diff --git a/edge/src/io/openems/impl/controller/supplybusswitch/Ess.java b/edge/src/io/openems/impl/controller/supplybusswitch/Ess.java index 8c199074169..756da963667 100644 --- a/edge/src/io/openems/impl/controller/supplybusswitch/Ess.java +++ b/edge/src/io/openems/impl/controller/supplybusswitch/Ess.java @@ -32,8 +32,6 @@ @IsThingMap(type = SymmetricEssNature.class) public class Ess extends ThingMap { - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; public final ReadChannel soc; public final ReadChannel activePower; public final ReadChannel reactivePower; @@ -61,8 +59,6 @@ public void setActiveSupplybus(Supplybus supplybus) { public Ess(SymmetricEssNature ess) { super(ess); - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); reactivePower = ess.reactivePower(); soc = ess.soc().required(); minSoc = ess.minSoc().required(); diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/AvoidTotalChargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/AvoidTotalChargeController.java index 614138012df..f982742d54d 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/AvoidTotalChargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/AvoidTotalChargeController.java @@ -129,21 +129,23 @@ public void run() { if (spareProducedPower < 0L){ try { Long totalActivePower = (long) (((double) ess.allowedCharge.value() / (double) avgAllowedCharge) * ((double) spareProducedPower / (double) esss.value().size())); - - ess.setActivePower.pushWrite(totalActivePower); + ess.activePowerLimit.setP(totalActivePower); + ess.power.applyLimitation(ess.activePowerLimit); } catch (Exception e) { log.error(e.getMessage(),e); } } else { try { - ess.setActivePower.pushWriteMin(0L); + ess.minActivePowerLimit.setP(0L); + ess.power.applyLimitation(ess.minActivePowerLimit); } catch (Exception e) { log.error(e.getMessage(),e); } } } else { try { - ess.setActivePower.pushWriteMin(0L); + ess.minActivePowerLimit.setP(0L); + ess.power.applyLimitation(ess.minActivePowerLimit); } catch (Exception e) { log.error(e.getMessage(),e); } diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java index 134eeb42aa1..37353438b3b 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java @@ -9,54 +9,60 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; import io.openems.api.controller.IsThingMap; import io.openems.api.controller.ThingMap; import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.core.utilities.hysteresis.Hysteresis; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; @IsThingMap(type = SymmetricEssNature.class) public class Ess extends ThingMap { - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final ReadChannel soc; - public final ReadChannel systemState; - public int maxPowerPercent = 100; - public final ReadChannel allowedDischarge; - public final ReadChannel allowedCharge; - public final ReadChannel chargeSoc; - public Hysteresis socMinHysteresis; - public State - currentState = State.NORMAL; - - public enum State { - NORMAL, MINSOC, CHARGESOC, FULL; - } - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - systemState = ess.systemState().required(); - soc = ess.soc().required(); - minSoc = ess.minSoc().required(); - allowedDischarge = ess.allowedDischarge().required(); - allowedCharge = ess.allowedCharge().required(); - chargeSoc = ess.chargeSoc().required(); - ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (minSoc.valueOptional().isPresent() && chargeSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(chargeSoc.valueOptional().get(), minSoc.valueOptional().get()); - } else if (minSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 3, minSoc.valueOptional().get()); - } - } - }; - minSoc.addChangeListener(hysteresisCreator); - chargeSoc.addChangeListener(hysteresisCreator); - - hysteresisCreator.channelChanged(null, null, null); - } + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel systemState; + public int maxPowerPercent = 100; + public final ReadChannel allowedDischarge; + public final ReadChannel allowedCharge; + public final ReadChannel chargeSoc; + public Hysteresis socMinHysteresis; + public SymmetricPower power; + public PEqualLimitation activePowerLimit; + public PGreaterEqualLimitation minActivePowerLimit; + public State + currentState = State.NORMAL; + + public enum State { + NORMAL, MINSOC, CHARGESOC, FULL; + } + + public Ess(SymmetricEssNature ess) { + super(ess); + systemState = ess.systemState().required(); + soc = ess.soc().required(); + minSoc = ess.minSoc().required(); + allowedDischarge = ess.allowedDischarge().required(); + allowedCharge = ess.allowedCharge().required(); + chargeSoc = ess.chargeSoc().required(); + power = ess.getPower(); + activePowerLimit = new PEqualLimitation(power); + minActivePowerLimit = new PGreaterEqualLimitation(power); + ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if (minSoc.valueOptional().isPresent() && chargeSoc.valueOptional().isPresent()) { + socMinHysteresis = new Hysteresis(chargeSoc.valueOptional().get(), minSoc.valueOptional().get()); + } else if (minSoc.valueOptional().isPresent()) { + socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 3, minSoc.valueOptional().get()); + } + } + }; + minSoc.addChangeListener(hysteresisCreator); + chargeSoc.addChangeListener(hysteresisCreator); + + hysteresisCreator.channelChanged(null, null, null); + } } \ No newline at end of file diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 0bdc89fa738..e52e83c3380 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -29,7 +29,7 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; +import io.openems.core.utilities.power.PowerException; import io.openems.impl.controller.symmetric.avoidtotaldischarge.Ess.State; @ThingInfo(title = "Avoid total discharge of battery (Symmetric)", description = "Makes sure the battery is not going into critically low state of charge. For symmetric Ess.") @@ -71,19 +71,10 @@ public void run() { ess.currentState = State.MINSOC; } else { try { - Optional currentMinValue = ess.setActivePower.writeMin(); - if (currentMinValue.isPresent() && currentMinValue.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Ess [" + ess.id() + "] force charge. Set ActivePower=Max[" - + currentMinValue.get() / 5 + "]"); - ess.setActivePower.pushWriteMax(currentMinValue.get() / 5); - } else { - log.info("Ess [" + ess.id() + "] Avoid discharge. Set ActivePower=Max[-1000 W]"); - ess.setActivePower.pushWriteMax(-1000L); - } - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + ess.maxActivePowerLimit.setP(ess.maxNominalPower.valueOptional().orElse(-1000L)); + ess.power.applyLimitation(ess.maxActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } break; @@ -93,14 +84,11 @@ public void run() { } else if (ess.soc.value() >= ess.minSoc.value() + 5) { ess.currentState = State.NORMAL; } else { + ess.maxActivePowerLimit.setP(0L); try { - long maxPower = 0; - if (!ess.setActivePower.writeMax().isPresent() - || maxPower < ess.setActivePower.writeMax().get()) { - ess.setActivePower.pushWriteMax(maxPower); - } - } catch (WriteChannelException e) { - log.error("Ess [" + ess.id() + "] Failed to set Max allowed power.", e); + ess.power.applyLimitation(ess.maxActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } break; @@ -113,11 +101,11 @@ public void run() { } break; case FULL: + ess.minActivePowerLimit.setP(0L); try { - ess.setActivePower.pushWriteMin(0L); - } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + ess.power.applyLimitation(ess.minActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } if (ess.soc.value() < maxSoc.value()) { ess.currentState = State.NORMAL; diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java index c4c806cb8fe..a20e3c72597 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java @@ -1,77 +1,85 @@ -/******************************************************************************* - * 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.symmetric.avoidtotaldischarge; - -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.hysteresis.Hysteresis; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final ReadChannel soc; - public final ReadChannel systemState; - public int maxPowerPercent = 100; - public final ReadChannel allowedDischarge; - public final ReadChannel allowedCharge; - public final ReadChannel chargeSoc; - public Hysteresis socMinHysteresis; - public State currentState = State.NORMAL; - - public enum State { - NORMAL, MINSOC, CHARGESOC, FULL; - } - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - systemState = ess.systemState().required(); - soc = ess.soc().required(); - minSoc = ess.minSoc().required(); - allowedDischarge = ess.allowedDischarge().required(); - allowedCharge = ess.allowedCharge().required(); - chargeSoc = ess.chargeSoc().required(); - ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (minSoc.valueOptional().isPresent() && chargeSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(chargeSoc.valueOptional().get(), minSoc.valueOptional().get()); - } else if (minSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 3, minSoc.valueOptional().get()); - } - } - }; - minSoc.addChangeListener(hysteresisCreator); - chargeSoc.addChangeListener(hysteresisCreator); - - hysteresisCreator.channelChanged(null, null, null); - } -} +/******************************************************************************* + * 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.symmetric.avoidtotaldischarge; + +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +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.SymmetricEssNature; +import io.openems.core.utilities.hysteresis.Hysteresis; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel systemState; + public int maxPowerPercent = 100; + public final ReadChannel allowedDischarge; + public final ReadChannel allowedCharge; + public final ReadChannel chargeSoc; + public Hysteresis socMinHysteresis; + public State currentState = State.NORMAL; + public final SymmetricPower power; + public final ReadChannel maxNominalPower; + public final PSmallerEqualLimitation maxActivePowerLimit; + public final PGreaterEqualLimitation minActivePowerLimit; + + public enum State { + NORMAL, MINSOC, CHARGESOC, FULL; + } + + public Ess(SymmetricEssNature ess) { + super(ess); + systemState = ess.systemState().required(); + soc = ess.soc().required(); + minSoc = ess.minSoc().required(); + allowedDischarge = ess.allowedDischarge().required(); + allowedCharge = ess.allowedCharge().required(); + chargeSoc = ess.chargeSoc().required(); + power = ess.getPower(); + maxActivePowerLimit = new PSmallerEqualLimitation(power); + minActivePowerLimit = new PGreaterEqualLimitation(power); + maxNominalPower = ess.maxNominalPower(); + ChannelChangeListener hysteresisCreator = new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if (minSoc.valueOptional().isPresent() && chargeSoc.valueOptional().isPresent()) { + socMinHysteresis = new Hysteresis(chargeSoc.valueOptional().get(), minSoc.valueOptional().get()); + } else if (minSoc.valueOptional().isPresent()) { + socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 3, minSoc.valueOptional().get()); + } + } + }; + minSoc.addChangeListener(hysteresisCreator); + chargeSoc.addChangeListener(hysteresisCreator); + + hysteresisCreator.channelChanged(null, null, null); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java index 2671f86bc9c..71436c42ed7 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java @@ -1,163 +1,152 @@ -/******************************************************************************* - * 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.symmetric.avoidtotaldischargesoctimeline; - -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; -import java.util.Optional; -import java.util.Set; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; -import io.openems.impl.controller.symmetric.avoidtotaldischargesoctimeline.Ess.State; - -@ThingInfo(title = "Avoid total discharge of battery (Symmetric)", description = "Makes sure the battery is not going into critically low state of charge. For symmetric Ess.") -public class AvoidTotalDischargeSocTimeLineController extends Controller implements ChannelChangeListener { - - /* - * Constructors - */ - public AvoidTotalDischargeSocTimeLineController() { - super(); - } - - public AvoidTotalDischargeSocTimeLineController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) - public final ConfigChannel> esss = new ConfigChannel>("esss", this).addChangeListener(this); - - @ChannelInfo(title = "Soc timeline", description = "This option configures an minsoc at a time for an ess. If no minsoc for an ess is configured the controller uses the minsoc of the ess.", type = JsonArray.class) - public final ConfigChannel socTimeline = new ConfigChannel("socTimeline", this) - .addChangeListener(this); - - /* - * Methods - */ - @Override - public void run() { - try { - LocalTime time = LocalTime.now(); - for (Ess ess : esss.value()) { - switch (ess.currentState) { - case CHARGESOC: - if (ess.soc.value() > ess.getMinSoc(time)) { - ess.currentState = State.MINSOC; - } else { - try { - Optional currentMinValue = ess.setActivePower.writeMin(); - if (currentMinValue.isPresent() && currentMinValue.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePower=Max[" + currentMinValue.get() / 5 + "]"); - ess.setActivePower.pushWriteMax(currentMinValue.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePower=Max[-1000 W]"); - ess.setActivePower.pushWriteMax(-1000L); - } - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - break; - case MINSOC: - if (ess.soc.value() < ess.getChargeSoc(time)) { - ess.currentState = State.CHARGESOC; - } else if (ess.soc.value() >= ess.getMinSoc(time) + 5) { - ess.currentState = State.NORMAL; - } else { - try { - long maxPower = 0; - if (!ess.setActivePower.writeMax().isPresent() - || maxPower < ess.setActivePower.writeMax().get()) { - ess.setActivePower.pushWriteMax(maxPower); - } - } catch (WriteChannelException e) { - log.error(ess.id() + "Failed to set Max allowed power.", e); - } - } - break; - case NORMAL: - if (ess.soc.value() <= ess.getMinSoc(time)) { - ess.currentState = State.MINSOC; - } - break; - } - - } - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - - private Ess getEss(String id) throws InvalidValueException { - for (Ess ess : esss.value()) { - if (ess.id().equals(id)) { - return ess; - } - } - return null; - } - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (channel.equals(esss) || channel.equals(socTimeline)) { - if (esss.valueOptional().isPresent() && socTimeline.valueOptional().isPresent() - && socTimeline.valueOptional().get() instanceof JsonArray) { - JsonArray timeline = socTimeline.valueOptional().get(); - for (JsonElement e : timeline) { - JsonObject obj = e.getAsJsonObject(); - int minSoc = obj.get("minSoc").getAsInt(); - int chargeSoc = obj.get("chargeSoc").getAsInt(); - LocalTime time = LocalTime.parse(obj.get("time").getAsString(), DateTimeFormatter.ISO_LOCAL_TIME); - JsonArray storages = obj.get("esss").getAsJsonArray(); - for (JsonElement storage : storages) { - Ess ess; - try { - ess = getEss(storage.getAsString()); - if (ess != null) { - ess.addTime(time, minSoc, chargeSoc); - } - } catch (InvalidValueException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - } - } - } - } - -} +/******************************************************************************* + * 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.symmetric.avoidtotaldischargesoctimeline; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Optional; +import java.util.Set; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; +import io.openems.impl.controller.symmetric.avoidtotaldischargesoctimeline.Ess.State; + +@ThingInfo(title = "Avoid total discharge of battery (Symmetric)", description = "Makes sure the battery is not going into critically low state of charge. For symmetric Ess.") +public class AvoidTotalDischargeSocTimeLineController extends Controller implements ChannelChangeListener { + + /* + * Constructors + */ + public AvoidTotalDischargeSocTimeLineController() { + super(); + } + + public AvoidTotalDischargeSocTimeLineController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) + public final ConfigChannel> esss = new ConfigChannel>("esss", this).addChangeListener(this); + + @ChannelInfo(title = "Soc timeline", description = "This option configures an minsoc at a time for an ess. If no minsoc for an ess is configured the controller uses the minsoc of the ess.", type = JsonArray.class) + public final ConfigChannel socTimeline = new ConfigChannel("socTimeline", this) + .addChangeListener(this); + + /* + * Methods + */ + @Override + public void run() { + try { + LocalTime time = LocalTime.now(); + for (Ess ess : esss.value()) { + switch (ess.currentState) { + case CHARGESOC: + if (ess.soc.value() > ess.getMinSoc(time)) { + ess.currentState = State.MINSOC; + } else { + try { + ess.maxActivePowerLimit.setP(ess.maxNominalPower.valueOptional().orElse(-1000L)); + ess.power.applyLimitation(ess.maxActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + break; + case MINSOC: + if (ess.soc.value() < ess.getChargeSoc(time)) { + ess.currentState = State.CHARGESOC; + } else if (ess.soc.value() >= ess.getMinSoc(time) + 5) { + ess.currentState = State.NORMAL; + } else { + ess.maxActivePowerLimit.setP(0L); + try { + ess.power.applyLimitation(ess.maxActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + break; + case NORMAL: + if (ess.soc.value() <= ess.getMinSoc(time)) { + ess.currentState = State.MINSOC; + } + break; + } + + } + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } + } + + private Ess getEss(String id) throws InvalidValueException { + for (Ess ess : esss.value()) { + if (ess.id().equals(id)) { + return ess; + } + } + return null; + } + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if (channel.equals(esss) || channel.equals(socTimeline)) { + if (esss.valueOptional().isPresent() && socTimeline.valueOptional().isPresent() + && socTimeline.valueOptional().get() instanceof JsonArray) { + JsonArray timeline = socTimeline.valueOptional().get(); + for (JsonElement e : timeline) { + JsonObject obj = e.getAsJsonObject(); + int minSoc = obj.get("minSoc").getAsInt(); + int chargeSoc = obj.get("chargeSoc").getAsInt(); + LocalTime time = LocalTime.parse(obj.get("time").getAsString(), DateTimeFormatter.ISO_LOCAL_TIME); + JsonArray storages = obj.get("esss").getAsJsonArray(); + for (JsonElement storage : storages) { + Ess ess; + try { + ess = getEss(storage.getAsString()); + if (ess != null) { + ess.addTime(time, minSoc, chargeSoc); + } + } catch (InvalidValueException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + } + } + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java index e06ab42d434..74006f23fb2 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java @@ -1,101 +1,109 @@ -/******************************************************************************* - * 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.symmetric.avoidtotaldischargesoctimeline; - -import java.time.LocalTime; -import java.util.Map; -import java.util.TreeMap; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final ReadChannel soc; - public final ReadChannel systemState; - public int maxPowerPercent = 100; - public final ReadChannel allowedDischarge; - public final ReadChannel chargeSoc; - private TreeMap timeline = new TreeMap<>(); - public State currentState = State.NORMAL; - - public enum State { - NORMAL, MINSOC, CHARGESOC - } - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - systemState = ess.systemState().required(); - soc = ess.soc().required(); - minSoc = ess.minSoc().required(); - allowedDischarge = ess.allowedDischarge().required(); - chargeSoc = ess.chargeSoc().required(); - } - - public void addTime(LocalTime time, int minSoc, int chargeSoc) { - Soc soc = new Soc(minSoc, chargeSoc); - timeline.put(time, soc); - } - - public int getMinSoc(LocalTime time) throws InvalidValueException { - Map.Entry entry = timeline.floorEntry(time); - if (entry != null) { - return entry.getValue().minSoc; - } - entry = timeline.lastEntry(); - if (entry != null) { - return entry.getValue().minSoc; - } - return minSoc.value(); - } - - public int getChargeSoc(LocalTime time) throws InvalidValueException { - Map.Entry entry = timeline.floorEntry(time); - if (entry != null) { - return entry.getValue().chargeSoc; - } - entry = timeline.lastEntry(); - if (entry != null) { - return entry.getValue().chargeSoc; - } - return chargeSoc.value(); - } - - private class Soc { - public final int minSoc; - public final int chargeSoc; - - public Soc(int minSoc, int chargeSoc) { - super(); - this.minSoc = minSoc; - this.chargeSoc = chargeSoc; - } - - } -} +/******************************************************************************* + * 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.symmetric.avoidtotaldischargesoctimeline; + +import java.time.LocalTime; +import java.util.Map; +import java.util.TreeMap; + +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.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel systemState; + public int maxPowerPercent = 100; + public final ReadChannel allowedDischarge; + public final ReadChannel chargeSoc; + private TreeMap timeline = new TreeMap<>(); + public State currentState = State.NORMAL; + public final SymmetricPower power; + public final ReadChannel maxNominalPower; + public final PSmallerEqualLimitation maxActivePowerLimit; + public final PGreaterEqualLimitation minActivePowerLimit; + + public enum State { + NORMAL, MINSOC, CHARGESOC + } + + public Ess(SymmetricEssNature ess) { + super(ess); + systemState = ess.systemState().required(); + soc = ess.soc().required(); + minSoc = ess.minSoc().required(); + allowedDischarge = ess.allowedDischarge().required(); + chargeSoc = ess.chargeSoc().required(); + power = ess.getPower(); + maxActivePowerLimit = new PSmallerEqualLimitation(power); + minActivePowerLimit = new PGreaterEqualLimitation(power); + maxNominalPower = ess.maxNominalPower(); + } + + public void addTime(LocalTime time, int minSoc, int chargeSoc) { + Soc soc = new Soc(minSoc, chargeSoc); + timeline.put(time, soc); + } + + public int getMinSoc(LocalTime time) throws InvalidValueException { + Map.Entry entry = timeline.floorEntry(time); + if (entry != null) { + return entry.getValue().minSoc; + } + entry = timeline.lastEntry(); + if (entry != null) { + return entry.getValue().minSoc; + } + return minSoc.value(); + } + + public int getChargeSoc(LocalTime time) throws InvalidValueException { + Map.Entry entry = timeline.floorEntry(time); + if (entry != null) { + return entry.getValue().chargeSoc; + } + entry = timeline.lastEntry(); + if (entry != null) { + return entry.getValue().chargeSoc; + } + return chargeSoc.value(); + } + + private class Soc { + public final int minSoc; + public final int chargeSoc; + + public Soc(int minSoc, int chargeSoc) { + super(); + this.minSoc = minSoc; + this.chargeSoc = chargeSoc; + } + + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java b/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java index c67333385cd..857fe927808 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java @@ -32,6 +32,7 @@ import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; import io.openems.core.utilities.AvgFiFoQueue; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Self-consumption optimization (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. Ess-Cluster is supported.") public class BalancingController extends Controller { @@ -77,8 +78,8 @@ public void run() { for (Ess ess : useableEss) { ess.powerAvg.add(ess.activePower.value()); calculatedPower += ess.powerAvg.avg(); - maxChargePower += ess.setActivePower.writeMin().orElse(ess.allowedCharge.value()); - maxDischargePower += ess.setActivePower.writeMax().orElse(ess.allowedDischarge.value()); + maxChargePower += ess.power.getMinP().orElse(0L); + maxDischargePower += ess.power.getMaxP().orElse(0L); useableSoc += ess.useableSoc(); } if (calculatedPower > 0) { @@ -103,14 +104,14 @@ public void run() { long minP = calculatedPower; for (int j = i + 1; j < useableEss.size(); j++) { if (useableEss.get(j).useableSoc() > 0) { - minP -= useableEss.get(j).allowedDischarge.value(); + minP -= useableEss.get(j).power.getMaxP().orElse(0L); } } if (minP < 0) { minP = 0; } // check maximal power to avoid larger charges then calculatedPower - long maxP = ess.allowedDischarge.value(); + long maxP = ess.power.getMaxP().orElse(0L); if (calculatedPower < maxP) { maxP = calculatedPower; } @@ -120,10 +121,8 @@ public void run() { * if the useableSoc is negative the ess will be charged */ long p = (long) (Math.ceil((minP + diff / useableSoc * ess.useableSoc()) / 100) * 100); - ess.power.setActivePower(p); - ess.power.writePower(); - log.debug(ess.id() + " Set ActivePower [" + ess.power.getActivePower() + "], ReactivePower [" - + ess.power.getReactivePower() + "]"); + ess.limit.setP(p); + ess.power.applyLimitation(ess.limit); calculatedPower -= p; } } else { @@ -153,13 +152,13 @@ public void run() { // calculate minimal power needed to fulfill the calculatedPower long minP = calculatedPower; for (int j = i + 1; j < useableEss.size(); j++) { - minP -= useableEss.get(j).allowedCharge.value(); + minP -= useableEss.get(j).power.getMinP().orElse(0L); } if (minP > 0) { minP = 0; } // check maximal power to avoid larger charges then calculatedPower - long maxP = ess.allowedCharge.value(); + long maxP = ess.power.getMinP().orElse(0L); if (calculatedPower > maxP) { maxP = calculatedPower; } @@ -168,10 +167,8 @@ public void run() { long p = (long) Math.floor( (minP + diff / (useableEss.size() * 100 - useableSoc) * (100 - ess.useableSoc())) / 100) * 100; - ess.power.setActivePower(p); - ess.power.writePower(); - log.debug(ess.id() + " Set ActivePower [" + ess.power.getActivePower() + "], ReactivePower [" - + ess.power.getReactivePower() + "]"); + ess.limit.setP(p); + ess.power.applyLimitation(ess.limit); calculatedPower -= p; } } @@ -179,6 +176,8 @@ public void run() { } } catch (InvalidValueException e) { log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java index 4007a216d68..e52952fc33a 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java @@ -21,20 +21,18 @@ package io.openems.impl.controller.symmetric.balancing; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; import io.openems.api.controller.IsThingMap; import io.openems.api.controller.ThingMap; import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.exception.InvalidValueException; import io.openems.core.utilities.AvgFiFoQueue; -import io.openems.core.utilities.SymmetricPower; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; @IsThingMap(type = SymmetricEssNature.class) public class Ess extends ThingMap { public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; public final ReadChannel soc; public final ReadChannel activePower; public final ReadChannel allowedCharge; @@ -43,22 +41,20 @@ public class Ess extends ThingMap { public final ReadChannel systemState; public final SymmetricPower power; public AvgFiFoQueue powerAvg = new AvgFiFoQueue(1, 1); + public final PEqualLimitation limit; public Ess(SymmetricEssNature ess) { super(ess); minSoc = ess.minSoc().required(); - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - soc = ess.soc().required(); activePower = ess.activePower().required(); allowedCharge = ess.allowedCharge().required(); allowedDischarge = ess.allowedDischarge().required(); gridMode = ess.gridMode().required(); systemState = ess.systemState().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); + this.power = ess.getPower(); + this.limit = new PEqualLimitation(power); } public long useableSoc() throws InvalidValueException { diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java new file mode 100644 index 00000000000..e281fadf3d6 --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * 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.symmetric.balancingbandgap; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.AvgFiFoQueue; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") +public class BalancingBandgapActivePowerController extends Controller { + + /* + * Constructors + */ + public BalancingBandgapActivePowerController() { + super(); + } + + public BalancingBandgapActivePowerController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Min-ActivePower", description = "Low boundary of active power bandgap.", type = Integer.class) + public final ConfigChannel minActivePower = new ConfigChannel<>("minActivePower", this); + + @ChannelInfo(title = "Max-ActivePower", description = "High boundary of active power bandgap.", type = Integer.class) + public final ConfigChannel maxActivePower = new ConfigChannel<>("maxActivePower", this); + + private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(2, 1.5); + private AvgFiFoQueue essActivePower = new AvgFiFoQueue(2, 1.5); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + Meter meter = this.meter.value(); + meterActivePower.add(meter.activePower.value()); + essActivePower.add(ess.activePower.value()); + // Calculate required sum values + long calculatedPower = meterActivePower.avg() + essActivePower.avg(); + if (calculatedPower >= maxActivePower.value()) { + calculatedPower -= maxActivePower.value(); + } else if (calculatedPower <= minActivePower.value()) { + calculatedPower -= minActivePower.value(); + } else { + calculatedPower = 0; + } + ess.activePowerLimit.setP(calculatedPower); + ess.power.applyLimitation(ess.activePowerLimit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("limit power failed!", e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java similarity index 58% rename from edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java rename to edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java index 9db242c4e3f..f9f44fe06e0 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java @@ -20,24 +20,27 @@ *******************************************************************************/ package io.openems.impl.controller.symmetric.balancingbandgap; +import java.util.NoSuchElementException; + import io.openems.api.channel.ConfigChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; import io.openems.core.utilities.AvgFiFoQueue; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") -public class BalancingBandgapController extends Controller { +public class BalancingBandgapReactivePowerController extends Controller { /* * Constructors */ - public BalancingBandgapController() { + public BalancingBandgapReactivePowerController() { super(); } - public BalancingBandgapController(String thingId) { + public BalancingBandgapReactivePowerController(String thingId) { super(thingId); } @@ -50,28 +53,13 @@ public BalancingBandgapController(String thingId) { @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) public final ConfigChannel meter = new ConfigChannel("meter", this); - @ChannelInfo(title = "Min-ActivePower", description = "Low boundary of active power bandgap.", type = Integer.class) - public final ConfigChannel minActivePower = new ConfigChannel<>("minActivePower", this); - - @ChannelInfo(title = "Max-ActivePower", description = "High boundary of active power bandgap.", type = Integer.class) - public final ConfigChannel maxActivePower = new ConfigChannel<>("maxActivePower", this); - @ChannelInfo(title = "Min-ReactivePower", description = "Low boundary of reactive power bandgap.", type = Integer.class) public final ConfigChannel minReactivePower = new ConfigChannel<>("minReactivePower", this); @ChannelInfo(title = "Max-ReactivePower", description = "High boundary of reactive power bandgap.", type = Integer.class) public final ConfigChannel maxReactivePower = new ConfigChannel<>("maxReactivePower", this); - @ChannelInfo(title = "Enable ActivePower", description = "Indicates if active power bandgap is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel activePowerActivated = new ConfigChannel("activePowerActivated", this); - - @ChannelInfo(title = "Enable ReactivePower", description = "Indicates if reactive power bandgap is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel reactivePowerActivated = new ConfigChannel("reactivePowerActivated", - this); - - private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(2, 1.5); private AvgFiFoQueue meterReactivePower = new AvgFiFoQueue(2, 1.5); - private AvgFiFoQueue essActivePower = new AvgFiFoQueue(2, 1.5); private AvgFiFoQueue essReactivePower = new AvgFiFoQueue(2, 1.5); /* @@ -82,20 +70,10 @@ public void run() { try { Ess ess = this.ess.value(); Meter meter = this.meter.value(); - meterActivePower.add(meter.activePower.value()); meterReactivePower.add(meter.reactivePower.value()); - essActivePower.add(ess.activePower.value()); essReactivePower.add(ess.reactivePower.value()); // Calculate required sum values - long calculatedPower = meterActivePower.avg() + essActivePower.avg(); long calculatedReactivePower = meterReactivePower.avg() + essReactivePower.avg(); - if (calculatedPower >= maxActivePower.value()) { - calculatedPower -= maxActivePower.value(); - } else if (calculatedPower <= minActivePower.value()) { - calculatedPower -= minActivePower.value(); - } else { - calculatedPower = 0; - } if (calculatedReactivePower >= maxReactivePower.value()) { calculatedReactivePower -= maxReactivePower.value(); } else if (calculatedReactivePower <= minReactivePower.value()) { @@ -103,24 +81,13 @@ public void run() { } else { calculatedReactivePower = 0; } - if (reactivePowerActivated.value()) { - ess.power.setReactivePower(calculatedReactivePower); - } - if (activePowerActivated.value()) { - ess.power.setActivePower(calculatedPower); - } - ess.power.writePower(); - // write info message to log - String message = ess.id(); - if (activePowerActivated.value()) { - message = message + " Set ActivePower [" + ess.power.getActivePower() + "]"; - } - if (reactivePowerActivated.value()) { - message = message + " Set ReactivePower [" + ess.power.getReactivePower() + "]"; - } - log.info(message); - } catch (InvalidValueException e) { + ess.reactivePowerLimit.setQ(calculatedReactivePower); + ess.power.applyLimitation(ess.reactivePowerLimit); + ; + } catch (InvalidValueException | NoSuchElementException e) { log.error(e.getMessage()); + } catch (PowerException e) { + log.error("failed to set ReactivePower!", e); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java index 7595add10b2..78b87063237 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java @@ -1,65 +1,64 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.balancingbandgap; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel soc; - public final ReadChannel activePower; - public final ReadChannel reactivePower; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final ReadChannel systemState; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - minSoc = ess.minSoc().required(); - - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - - soc = ess.soc().required(); - activePower = ess.activePower().required(); - allowedCharge = ess.allowedCharge().required(); - allowedDischarge = ess.allowedDischarge().required(); - systemState = ess.systemState().required(); - reactivePower = ess.reactivePower().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - - public long useableSoc() throws InvalidValueException { - return soc.value() - minSoc.value(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.balancingbandgap; + +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.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.QEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel activePower; + public final ReadChannel reactivePower; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final ReadChannel systemState; + public final SymmetricPower power; + public final PEqualLimitation activePowerLimit; + public final QEqualLimitation reactivePowerLimit; + + public Ess(SymmetricEssNature ess) { + super(ess); + minSoc = ess.minSoc().required(); + + soc = ess.soc().required(); + activePower = ess.activePower().required(); + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + systemState = ess.systemState().required(); + reactivePower = ess.reactivePower().required(); + this.power = ess.getPower(); + activePowerLimit = new PEqualLimitation(power); + reactivePowerLimit = new QEqualLimitation(power); + } + + public long useableSoc() throws InvalidValueException { + return soc.value() - minSoc.value(); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java index 81b225b0aaa..052481afb01 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java @@ -25,7 +25,7 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Balancing Cos-Phi (Symmetric)", description = "Tries to keep the grid meter at a given cos-phi. For symmetric Ess.") public class BalancingCosPhiController extends Controller { @@ -64,18 +64,15 @@ public void run() { try { Ess ess = this.ess.value(); Meter meter = this.meter.value(); - long currentActivePowerEss = ess.activePower.value();//50 - long currentReactivePowerEss = ess.activePower.value();//10 - long currentActivePowerGrid = meter.activePower.value();//-10 - long currentReactivePowerGrid = meter.reactivePower.value();//5 - long expectedActivePowerGrid = currentActivePowerGrid-(ess.setActivePower.getWriteValue().orElse(currentActivePowerEss)-currentActivePowerEss);//-10-(-2-50)=-62 - long expectedReactivePowerGrid = ControllerUtils.calculateReactivePower(expectedActivePowerGrid, cosPhi.value(),capacitive.value());//30,027 - long q = currentReactivePowerEss - (expectedReactivePowerGrid - currentReactivePowerGrid);//10-(30,027-5)=-15,02 - ess.power.setReactivePower(q); - ess.power.writePower(); - log.info(ess.id() + " Set ReactivePower [" + ess.power.getReactivePower() + "]"); + //Calculate the startpoint of the cosPhi line in relation to the ess zero power + long pNull = (meter.activePower.value()-ess.activePower.value())*-1; + long qNull = (meter.reactivePower.value()-ess.reactivePower.value())*-1; + ess.limit.setCosPhi(cosPhi.value(), capacitive.value(), pNull, qNull); + ess.power.applyLimitation(ess.limit); } catch (InvalidValueException e) { log.error("Failed to read value.", e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } } diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java index c50dd585926..cf23acdf295 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java @@ -21,35 +21,32 @@ package io.openems.impl.controller.symmetric.balancingcosphi; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; import io.openems.api.controller.IsThingMap; import io.openems.api.controller.ThingMap; import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; +import io.openems.core.utilities.power.CosPhiLineLimitation; +import io.openems.core.utilities.power.SymmetricPower; @IsThingMap(type = SymmetricEssNature.class) public class Ess extends ThingMap { - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; public final ReadChannel reactivePower; public final ReadChannel activePower; public final String id; public final ReadChannel allowedCharge; public final ReadChannel allowedDischarge; public final SymmetricPower power; + public final CosPhiLineLimitation limit; public Ess(SymmetricEssNature ess) { super(ess); - setActivePower = ess.setActivePower(); - setReactivePower = ess.setReactivePower(); id = ess.id(); allowedCharge = ess.allowedCharge(); allowedDischarge = ess.allowedDischarge(); reactivePower = ess.reactivePower(); activePower = ess.activePower(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); + this.power = ess.getPower(); + this.limit = new CosPhiLineLimitation(power); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java index 39030e630ee..4ed19241cc7 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java @@ -1,91 +1,93 @@ -/******************************************************************************* - * 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.symmetric.balancingcurrent; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; - -@ThingInfo(title = "Balancing current (Symmetric)", description = "Tries to keep the grid meter at a given current. For symmetric Ess.") -public class BalancingCurrentController extends Controller { - - /* - * Constructors - */ - public BalancingCurrentController() { - super(); - } - - public BalancingCurrentController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) - public final ConfigChannel meter = new ConfigChannel("meter", this); - - @ChannelInfo(title = "Current offset", description = "The current to hold on the grid-meter.", type = Meter.class) - public final ConfigChannel currentOffset = new ConfigChannel<>("CurrentOffset", this); - - /* - * Methods - */ - @Override - public void run() { - try { - Ess ess = this.ess.value(); - // Calculate required sum values - long power = calculatePower() + ess.activePower.value(); - ess.power.setActivePower(power); - ess.power.writePower(); - log.info(ess.id() + " Set ActivePower [" + ess.power.getActivePower() + "]"); - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - - private long calculatePower() throws InvalidValueException { - long currentL1 = meter.value().currentL1.value(); - if (meter.value().activePowerL1.value() < 0) { - currentL1 *= -1; - } - long powerL1 = ((currentL1 - currentOffset.value() / 3) / 1000) * (meter.value().voltageL1.value() / 1000); - long currentL2 = meter.value().currentL2.value(); - if (meter.value().activePowerL2.value() < 0) { - currentL2 *= -1; - } - long powerL2 = ((currentL2 - currentOffset.value() / 3) / 1000) * (meter.value().voltageL2.value() / 1000); - long currentL3 = meter.value().currentL3.value(); - if (meter.value().activePowerL3.value() < 0) { - currentL3 *= -1; - } - long powerL3 = ((currentL3 - currentOffset.value() / 3) / 1000) * (meter.value().voltageL3.value() / 1000); - return powerL1 + powerL2 + powerL3; - } - -} +/******************************************************************************* + * 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.symmetric.balancingcurrent; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Balancing current (Symmetric)", description = "Tries to keep the grid meter at a given current. For symmetric Ess.") +public class BalancingCurrentController extends Controller { + + /* + * Constructors + */ + public BalancingCurrentController() { + super(); + } + + public BalancingCurrentController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Current offset", description = "The current to hold on the grid-meter.", type = Meter.class) + public final ConfigChannel currentOffset = new ConfigChannel<>("CurrentOffset", this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + // Calculate required sum values + long power = calculatePower() + ess.activePower.value(); + ess.limit.setP(power); + ess.power.applyLimitation(ess.limit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power",e); + } + } + + private long calculatePower() throws InvalidValueException { + long currentL1 = meter.value().currentL1.value(); + if (meter.value().activePowerL1.value() < 0) { + currentL1 *= -1; + } + long powerL1 = ((currentL1 - currentOffset.value() / 3) / 1000) * (meter.value().voltageL1.value() / 1000); + long currentL2 = meter.value().currentL2.value(); + if (meter.value().activePowerL2.value() < 0) { + currentL2 *= -1; + } + long powerL2 = ((currentL2 - currentOffset.value() / 3) / 1000) * (meter.value().voltageL2.value() / 1000); + long currentL3 = meter.value().currentL3.value(); + if (meter.value().activePowerL3.value() < 0) { + currentL3 *= -1; + } + long powerL3 = ((currentL3 - currentOffset.value() / 3) / 1000) * (meter.value().voltageL3.value() / 1000); + return powerL1 + powerL2 + powerL3; + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java index c36ba51ffaa..eedc9537cfe 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java @@ -1,59 +1,55 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.balancingcurrent; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final ReadChannel systemState; - public final ReadChannel apparentPower; - public final ReadChannel activePower; - public final ReadChannel reactivePower; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - - allowedCharge = ess.allowedCharge().required(); - allowedDischarge = ess.allowedDischarge().required(); - systemState = ess.systemState().required(); - apparentPower = ess.apparentPower().required(); - activePower = ess.activePower().required(); - reactivePower = ess.reactivePower().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.balancingcurrent; + +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.SymmetricEssNature; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final ReadChannel systemState; + public final ReadChannel apparentPower; + public final ReadChannel activePower; + public final ReadChannel reactivePower; + public final SymmetricPower power; + public final PEqualLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + systemState = ess.systemState().required(); + apparentPower = ess.apparentPower().required(); + activePower = ess.activePower().required(); + reactivePower = ess.reactivePower().required(); + power = ess.getPower(); + this.limit = new PEqualLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java new file mode 100644 index 00000000000..c95fd2487ed --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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.symmetric.balancingoffset; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +/* + * this Controller calculates the power consumption of the house and charges or discharges the storages to reach zero power consumption from the grid + */ +@ThingInfo(title = "Balancing offset (Symmetric)", description = "Tries to keep the grid meter within an offset. For symmetric Ess.") +public class BalancingOffsetActivePowerController extends Controller { + + /* + * Constructors + */ + public BalancingOffsetActivePowerController() { + super(); + // init(); + } + + public BalancingOffsetActivePowerController(String thingId) { + super(thingId); + // init(); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel<>("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel<>("meter", this); + + @ChannelInfo(title = "Offset ActivePower", description = "The offset of the active power from zero to hold on the grid meter.", type = Integer.class) + public final ConfigChannel activePowerOffset = new ConfigChannel<>("activePowerOffset", this); + + /* + * Methods + */ + + @Override + public void run() { + try { + Ess ess = this.ess.value(); + // Calculate required sum values + long calculatedPower = meter.value().activePower.value() + ess.activePower.value() + - activePowerOffset.value(); + ess.activePowerLimit.setP(calculatedPower); + ess.power.applyLimitation(ess.activePowerLimit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetController.java deleted file mode 100644 index bdbe77a7857..00000000000 --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetController.java +++ /dev/null @@ -1,138 +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.symmetric.balancingoffset; - -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; - -/* - * this Controller calculates the power consumption of the house and charges or discharges the storages to reach zero power consumption from the grid - */ -@ThingInfo(title = "Balancing offset (Symmetric)", description = "Tries to keep the grid meter within an offset. For symmetric Ess.") -public class BalancingOffsetController extends Controller { - - /* - * Constructors - */ - public BalancingOffsetController() { - super(); - // init(); - } - - public BalancingOffsetController(String thingId) { - super(thingId); - // init(); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel<>("ess", this); - - @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) - public final ConfigChannel meter = new ConfigChannel<>("meter", this); - - @ChannelInfo(title = "Offset ActivePower", description = "The offset of the active power from zero to hold on the grid meter.", type = Integer.class) - public final ConfigChannel activePowerOffset = new ConfigChannel<>("activePowerOffset", this); - - @ChannelInfo(title = "Offset ReactivePower", description = "The offset of the reactive power from zero to hold on the grid meter.", type = Integer.class) - public final ConfigChannel reactivePowerOffset = new ConfigChannel<>("reactivePowerOffset", this); - - @ChannelInfo(title = "Enable ActivePower", description = "Indicates if active power is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel activePowerActivated = new ConfigChannel("activePowerActivated", this); - - @ChannelInfo(title = "Enable ReactivePower", description = "Indicates if reactive power is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel reactivePowerActivated = new ConfigChannel("reactivePowerActivated", - this); - - /* - * Methods - */ - @Override - public void init() { - activePowerActivated.addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - try { - if (!activePowerActivated.value()) { - ess.value().setActivePower.pushWrite(0L); - } - } catch (WriteChannelException | InvalidValueException e) { - log.error(e.getMessage()); - } - } - }); - reactivePowerActivated.addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - try { - if (!reactivePowerActivated.value()) { - ess.value().setReactivePower.pushWrite(0L); - } - } catch (WriteChannelException | InvalidValueException e) { - log.error(e.getMessage()); - } - } - }); - } - - @Override - public void run() { - try { - Ess ess = this.ess.value(); - // Calculate required sum values - long calculatedPower = meter.value().activePower.value() + ess.activePower.value() - - activePowerOffset.value(); - long calculatedReactivePower = meter.value().reactivePower.value() + ess.reactivePower.value() - - reactivePowerOffset.value(); - if (reactivePowerActivated.value()) { - ess.power.setReactivePower(calculatedReactivePower); - } - if (activePowerActivated.value()) { - ess.power.setActivePower(calculatedPower); - } - ess.power.writePower(); - // print info message to log - String message = ess.id(); - if (activePowerActivated.value()) { - message = message + " Set ActivePower [" + ess.power.getActivePower() + "]"; - } - if (reactivePowerActivated.value()) { - message = message + " Set ReactivePower [" + ess.power.getReactivePower() + "]"; - } - log.info(message); - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - -} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java new file mode 100644 index 00000000000..908005e8bfb --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.symmetric.balancingoffset; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +/* + * this Controller calculates the power consumption of the house and charges or discharges the storages to reach zero power consumption from the grid + */ +@ThingInfo(title = "Balancing offset (Symmetric)", description = "Tries to keep the grid meter within an offset. For symmetric Ess.") +public class BalancingOffsetReactivePowerController extends Controller { + + /* + * Constructors + */ + public BalancingOffsetReactivePowerController() { + super(); + } + + public BalancingOffsetReactivePowerController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel<>("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel<>("meter", this); + + @ChannelInfo(title = "Offset ReactivePower", description = "The offset of the reactive power from zero to hold on the grid meter.", type = Integer.class) + public final ConfigChannel reactivePowerOffset = new ConfigChannel<>("reactivePowerOffset", this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + // Calculate required sum values + long calculatedReactivePower = meter.value().reactivePower.value() + ess.reactivePower.value() + - reactivePowerOffset.value(); + ess.reactivePowerLimit.setQ(calculatedReactivePower); + ess.power.applyLimitation(ess.reactivePowerLimit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java index ca76d45d23d..8b39bb0ccd6 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java @@ -1,65 +1,64 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.balancingoffset; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel soc; - public final ReadChannel activePower; - public final ReadChannel reactivePower; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final ReadChannel systemState; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - minSoc = ess.minSoc().required(); - - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - - soc = ess.soc().required(); - activePower = ess.activePower().required(); - allowedCharge = ess.allowedCharge().required(); - allowedDischarge = ess.allowedDischarge().required(); - systemState = ess.systemState().required(); - reactivePower = ess.reactivePower().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - - public long useableSoc() throws InvalidValueException { - return soc.value() - minSoc.value(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.balancingoffset; + +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.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.QEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel activePower; + public final ReadChannel reactivePower; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final ReadChannel systemState; + public final SymmetricPower power; + public final PEqualLimitation activePowerLimit; + public final QEqualLimitation reactivePowerLimit; + + public Ess(SymmetricEssNature ess) { + super(ess); + minSoc = ess.minSoc().required(); + + soc = ess.soc().required(); + activePower = ess.activePower().required(); + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + systemState = ess.systemState().required(); + reactivePower = ess.reactivePower().required(); + this.power = ess.getPower(); + this.activePowerLimit = new PEqualLimitation(power); + this.reactivePowerLimit = new QEqualLimitation(power); + } + + public long useableSoc() throws InvalidValueException { + return soc.value() - minSoc.value(); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java index 58bd5ec0e92..95ae226accb 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java @@ -1,124 +1,127 @@ -/******************************************************************************* - * 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.symmetric.balancingsurplus; - -import java.util.Set; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.AvgFiFoQueue; - -@ThingInfo(title = "Self-consumption optimization with surplus feed-in (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. If ess is over the surplusMinSoc, the ess discharges with the power of the chargers. ") -public class BalancingSurplusController extends Controller { - - /* - * Constructors - */ - public BalancingSurplusController() { - super(); - } - - public BalancingSurplusController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Charger", description = "Sets the Chargers connected to the ess.", type = Charger.class, isArray = true) - public final ConfigChannel> chargers = new ConfigChannel>("chargers", this); - - @ChannelInfo(title = "Surplus min soc", description = "The required Soc to start surplus feed-in.", type = Long.class) - public final ConfigChannel surplusMinSoc = new ConfigChannel("surplusMinSoc", this); - - @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) - public final ConfigChannel meter = new ConfigChannel("meter", this); - - private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(3, 1.5); - private AvgFiFoQueue essActivePower = new AvgFiFoQueue(3, 1.5); - - private long surplus = 0L; - private boolean surplusOn = false; - - /* - * Methods - */ - - @Override - public void run() { - try { - Ess ess = this.ess.value(); - meterActivePower.add(meter.value().activePower.value()); - essActivePower.add((ess.activePower.value() - surplus)); - // Calculate required sum values - long calculatedPower = meterActivePower.avg() + essActivePower.avg(); - surplus = getSurplusPower(); - // in case the storage has surplus it isn't allowed to charge the storage ac - if (calculatedPower < 0 && surplus > 0) { - calculatedPower = 0; - } - if (getPvVoltage() < 200000 || surplus < 0) { - surplus = 0l; - } - calculatedPower += surplus; - ess.power.setActivePower(calculatedPower); - ess.power.writePower(); - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - - private long getSurplusPower() throws InvalidValueException { - long power = 0l; - if (ess.value().soc.value() >= surplusMinSoc.value() + 2) { - surplusOn = true; - } else if (ess.value().soc.value() < surplusMinSoc.value()) { - surplusOn = false; - } - if (surplusOn) { - for (Charger c : chargers.value()) { - power += c.power.value(); - } - long multiplier = ess.value().soc.value() - surplusMinSoc.value() - 2; - if (multiplier > 0) { - power += ess.value().nominalPower.value() * 0.25 / (100 - surplusMinSoc.value() - 2) * multiplier; - } - } - return power; - } - - private long getPvVoltage() throws InvalidValueException { - long voltage = 0; - for (Charger c : chargers.value()) { - if (c.inputVoltage.value() > voltage) { - voltage = c.inputVoltage.value(); - } - } - return voltage; - } - -} +/******************************************************************************* + * 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.symmetric.balancingsurplus; + +import java.util.Set; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.AvgFiFoQueue; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Self-consumption optimization with surplus feed-in (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. If ess is over the surplusMinSoc, the ess discharges with the power of the chargers. ") +public class BalancingSurplusController extends Controller { + + /* + * Constructors + */ + public BalancingSurplusController() { + super(); + } + + public BalancingSurplusController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Charger", description = "Sets the Chargers connected to the ess.", type = Charger.class, isArray = true) + public final ConfigChannel> chargers = new ConfigChannel>("chargers", this); + + @ChannelInfo(title = "Surplus min soc", description = "The required Soc to start surplus feed-in.", type = Long.class) + public final ConfigChannel surplusMinSoc = new ConfigChannel("surplusMinSoc", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(3, 1.5); + private AvgFiFoQueue essActivePower = new AvgFiFoQueue(3, 1.5); + + private long surplus = 0L; + private boolean surplusOn = false; + + /* + * Methods + */ + + @Override + public void run() { + try { + Ess ess = this.ess.value(); + meterActivePower.add(meter.value().activePower.value()); + essActivePower.add((ess.activePower.value() - surplus)); + // Calculate required sum values + long calculatedPower = meterActivePower.avg() + essActivePower.avg(); + surplus = getSurplusPower(); + // in case the storage has surplus it isn't allowed to charge the storage ac + if (calculatedPower < 0 && surplus > 0) { + calculatedPower = 0; + } + if (getPvVoltage() < 200000 || surplus < 0) { + surplus = 0l; + } + calculatedPower += surplus; + ess.limit.setP(calculatedPower); + ess.power.applyLimitation(ess.limit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + + private long getSurplusPower() throws InvalidValueException { + long power = 0l; + if (ess.value().soc.value() >= surplusMinSoc.value() + 2) { + surplusOn = true; + } else if (ess.value().soc.value() < surplusMinSoc.value()) { + surplusOn = false; + } + if (surplusOn) { + for (Charger c : chargers.value()) { + power += c.power.value(); + } + long multiplier = ess.value().soc.value() - surplusMinSoc.value() - 2; + if (multiplier > 0) { + power += ess.value().nominalPower.value() * 0.25 / (100 - surplusMinSoc.value() - 2) * multiplier; + } + } + return power; + } + + private long getPvVoltage() throws InvalidValueException { + long voltage = 0; + for (Charger c : chargers.value()) { + if (c.inputVoltage.value() > voltage) { + voltage = c.inputVoltage.value(); + } + } + return voltage; + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java index fd1af55b99d..c19721b2f65 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java @@ -1,66 +1,63 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.balancingsurplus; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel soc; - public final ReadChannel activePower; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final ReadChannel gridMode; - public final ReadChannel systemState; - public final ReadChannel nominalPower; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - minSoc = ess.minSoc().required(); - - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - this.nominalPower = ess.maxNominalPower().required(); - soc = ess.soc().required(); - activePower = ess.activePower().required(); - allowedCharge = ess.allowedCharge().required(); - allowedDischarge = ess.allowedDischarge().required(); - gridMode = ess.gridMode().required(); - systemState = ess.systemState().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - - public long useableSoc() throws InvalidValueException { - return soc.value() - minSoc.value(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.balancingsurplus; + +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.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel activePower; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final ReadChannel gridMode; + public final ReadChannel systemState; + public final ReadChannel nominalPower; + public final SymmetricPower power; + public final PEqualLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + minSoc = ess.minSoc().required(); + + this.nominalPower = ess.maxNominalPower().required(); + soc = ess.soc().required(); + activePower = ess.activePower().required(); + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + gridMode = ess.gridMode().required(); + systemState = ess.systemState().required(); + this.power = ess.getPower(); + this.limit = new PEqualLimitation(power); + } + + public long useableSoc() throws InvalidValueException { + return soc.value() - minSoc.value(); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java b/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java index 466d6a4563e..ac4e593a391 100644 --- a/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java +++ b/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java @@ -1,149 +1,155 @@ -/******************************************************************************* - * 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.symmetric.capacitytest; - -import java.io.FileWriter; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelUpdateListener; -import io.openems.api.channel.ConfigChannel; -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.InvalidValueException; -import io.openems.api.exception.WriteChannelException; - -@ThingInfo(title = "Battery capacity test (Symmetric)", description = "Executes a capacity test. For symmetric Ess.") -public class CapacityTestController extends Controller { - - /* - * Constructors - */ - public CapacityTestController() { - super(); - initialize(); - } - - public CapacityTestController(String thingId) { - super(thingId); - initialize(); - } - - /* - * Config - */ - @ChannelInfo(title = "Power", description = "Discharge power of Ess.", type = Integer.class, defaultValue = "750") - public ConfigChannel power = new ConfigChannel("power", this); - - @ChannelInfo(title = "Sleep", description = "Time to sleep after empty ess before start capacityTest.", type = Integer.class, defaultValue = "750") - public ConfigChannel sleep = new ConfigChannel("sleep", this); - - @ChannelInfo(title = "Log-File", description = "Path to save the logfile.", type = String.class) - public ConfigChannel logPath = new ConfigChannel("logPath", this); - - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) - public ConfigChannel> esss = new ConfigChannel>("esss", this); - - private Long start = null; - - /* - * Fields - */ - private FileWriter fw; - - /* - * Methods - */ - private void initialize() { - logPath.addUpdateListener(new ChannelUpdateListener() { - - @Override - public void channelUpdated(Channel channel, Optional newValue) { - try { - if (fw != null) { - fw.close(); - } - fw = new FileWriter(logPath.value()); - fw.write("time;activePower;soc\n"); - } catch (IOException e) { - log.error(e.getMessage()); - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - - } - }); - } - - @Override - public void run() { - if (start == null) { - start = System.currentTimeMillis(); - } - if (start != null && start + 5000 <= System.currentTimeMillis()) { - try { - for (Ess ess : esss.value()) { - ess.setWorkState.pushWriteFromLabel(EssNature.START); - if (ess.empty) { - if (ess.timeEmpty + sleep.value() <= System.currentTimeMillis()) { - // Capacitytest - if (ess.full) { - // fully discharge ess - ess.setActivePower.pushWrite((long) power.value()); - } else { - // fully charge ess - ess.setActivePower.pushWrite((long) power.value() * -1); - if (ess.allowedCharge.value() >= -100l - && ess.systemState.labelOptional().equals(Optional.of(EssNature.START))) { - ess.full = true; - } - } - fw.append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) - + ";" + ess.activePower.value() + ";" + ess.soc.value() + "\n"); - fw.flush(); - } - } else { - // prepare for capacityTest - // Empty ess - ess.setActivePower.pushWrite(ess.allowedDischarge.value()); - if (ess.soc.value() <= ess.minSoc.value()) { - ess.empty = true; - ess.timeEmpty = System.currentTimeMillis(); - } - } - } - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } catch (WriteChannelException e) { - log.error(e.getMessage()); - } catch (IOException e) { - log.error(e.getMessage()); - } - } - } - -} +/******************************************************************************* + * 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.symmetric.capacitytest; + +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelUpdateListener; +import io.openems.api.channel.ConfigChannel; +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.InvalidValueException; +import io.openems.api.exception.WriteChannelException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Battery capacity test (Symmetric)", description = "Executes a capacity test. For symmetric Ess.") +public class CapacityTestController extends Controller { + + /* + * Constructors + */ + public CapacityTestController() { + super(); + initialize(); + } + + public CapacityTestController(String thingId) { + super(thingId); + initialize(); + } + + /* + * Config + */ + @ChannelInfo(title = "Power", description = "Discharge power of Ess.", type = Integer.class, defaultValue = "750") + public ConfigChannel power = new ConfigChannel("power", this); + + @ChannelInfo(title = "Sleep", description = "Time to sleep after empty ess before start capacityTest.", type = Integer.class, defaultValue = "750") + public ConfigChannel sleep = new ConfigChannel("sleep", this); + + @ChannelInfo(title = "Log-File", description = "Path to save the logfile.", type = String.class) + public ConfigChannel logPath = new ConfigChannel("logPath", this); + + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) + public ConfigChannel> esss = new ConfigChannel>("esss", this); + + private Long start = null; + + /* + * Fields + */ + private FileWriter fw; + + /* + * Methods + */ + private void initialize() { + logPath.addUpdateListener(new ChannelUpdateListener() { + + @Override + public void channelUpdated(Channel channel, Optional newValue) { + try { + if (fw != null) { + fw.close(); + } + fw = new FileWriter(logPath.value()); + fw.write("time;activePower;soc\n"); + } catch (IOException e) { + log.error(e.getMessage()); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } + + } + }); + } + + @Override + public void run() { + if (start == null) { + start = System.currentTimeMillis(); + } + if (start != null && start + 5000 <= System.currentTimeMillis()) { + try { + for (Ess ess : esss.value()) { + ess.setWorkState.pushWriteFromLabel(EssNature.START); + if (ess.empty) { + if (ess.timeEmpty + sleep.value() <= System.currentTimeMillis()) { + // Capacitytest + if (ess.full) { + // fully discharge ess + ess.limit.setP((long)power.value()); + ess.power.applyLimitation(ess.limit); + } else { + // fully charge ess + ess.limit.setP((long) power.value() * -1); + ess.power.applyLimitation(ess.limit); + if (ess.allowedCharge.value() >= -100l + && ess.systemState.labelOptional().equals(Optional.of(EssNature.START))) { + ess.full = true; + } + } + fw.append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + + ";" + ess.activePower.value() + ";" + ess.soc.value() + "\n"); + fw.flush(); + } + } else { + // prepare for capacityTest + // Empty ess + ess.limit.setP(ess.allowedDischarge.value()); + ess.power.applyLimitation(ess.limit); + if (ess.soc.value() <= ess.minSoc.value()) { + ess.empty = true; + ess.timeEmpty = System.currentTimeMillis(); + } + } + } + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (IOException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } catch (WriteChannelException e) { + log.error(e.getMessage()); + } + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java b/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java index 567f1491324..78cd855acb3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java @@ -1,56 +1,60 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.capacitytest; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - public ReadChannel soc; - public ReadChannel activePower; - public ReadChannel allowedCharge; - public ReadChannel allowedDischarge; - public ReadChannel minSoc; - public WriteChannel setWorkState; - public WriteChannel setActivePower; - public ReadChannel allowedApparent; - public ReadChannel systemState; - public boolean empty = false; - public boolean full = false; - public long timeEmpty = 0; - - public Ess(SymmetricEssNature ess) { - super(ess); - activePower = ess.activePower().required(); - allowedCharge = ess.allowedCharge().required(); - allowedDischarge = ess.allowedDischarge().required(); - minSoc = ess.minSoc().required(); - setActivePower = ess.setActivePower().required(); - soc = ess.soc().required(); - setWorkState = ess.setWorkState().required(); - allowedApparent = ess.allowedApparent().required(); - systemState = ess.systemState().required(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.capacitytest; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + public ReadChannel soc; + public ReadChannel activePower; + public ReadChannel allowedCharge; + public ReadChannel allowedDischarge; + public ReadChannel minSoc; + public WriteChannel setWorkState; + public ReadChannel allowedApparent; + public ReadChannel systemState; + public boolean empty = false; + public boolean full = false; + public long timeEmpty = 0; + public SymmetricPower power; + public PEqualLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + activePower = ess.activePower().required(); + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + minSoc = ess.minSoc().required(); + soc = ess.soc().required(); + setWorkState = ess.setWorkState().required(); + allowedApparent = ess.allowedApparent().required(); + systemState = ess.systemState().required(); + power = ess.getPower(); + limit = new PEqualLimitation(power); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/EnergysavingController.java b/edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/EnergysavingController.java deleted file mode 100644 index 4e08618940f..00000000000 --- a/edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/EnergysavingController.java +++ /dev/null @@ -1,116 +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.symmetric.commercialenergysaver; - -import java.util.Optional; -import java.util.Set; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; - -/** - * @author matthias.rossmann - */ -@ThingInfo(title = "Energy saving (Symmetric)", description = "Sends the Ess to Standby if no power is required for two minutes. Do not use if Off-Grid functionality is required. For symmetric Ess.") -public class EnergysavingController extends Controller { - - /* - * Constructors - */ - public EnergysavingController() { - super(); - } - - public EnergysavingController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) - public final ConfigChannel> esss = new ConfigChannel>("esss", this); - - /* - * Fields - */ - private Long lastTimeValueWritten = 0L; - - /* - * Methods - */ - @Override - public void run() { - try { - for (Ess ess : esss.value()) { - try { - Optional systemState = ess.systemState.labelOptional(); - if (systemState.isPresent()) { - if ((ess.setActivePower.peekWrite().isPresent() && ess.setActivePower.peekWrite().get() != 0) - || (ess.setReactivePower.peekWrite().isPresent() - && ess.setReactivePower.peekWrite().get() != 0)) { - if (!systemState.get().equals(SymmetricEssNature.START)) { - // Current system state is not START - if (ess.setWorkState.peekWriteLabel().orElse(SymmetricEssNature.START) - .equals(SymmetricEssNature.START)) { - // SetWorkState was not set to anything different than START before -> START the - // system - log.info("ESS [" + ess.id() + "] was stopped. Starting..."); - ess.setWorkState.pushWriteFromLabel(SymmetricEssNature.START); - } - } - lastTimeValueWritten = System.currentTimeMillis(); - } else { - /* - * go to Standby if no values were written since two minutes - */ - if (lastTimeValueWritten + 2 * 60 * 1000 < System.currentTimeMillis()) { - if (!systemState.isPresent() || (!systemState.get().equals(SymmetricEssNature.STANDBY) - && !systemState.get().equals("PV-Charge"))) { - // System state was not yet STANDBY or PV-Charge - if (ess.setWorkState.peekWriteLabel().orElse(SymmetricEssNature.STANDBY) - .equals(SymmetricEssNature.STANDBY)) { - // SetWorkState was not set to anything different than STANDBY before -> put the - // system - // in STANDBY - log.info("ESS [" + ess.id() - + "] had no written value since two minutes. Standby..."); - ess.setWorkState.pushWriteFromLabel(SymmetricEssNature.STANDBY); - } - } - } - } - } - } catch (WriteChannelException e) { - log.error("", e); - } - } - } catch (InvalidValueException e) { - log.error("", e); - } - } - -} diff --git a/edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/Ess.java b/edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/Ess.java deleted file mode 100644 index 16efee7e8cf..00000000000 --- a/edge/src/io/openems/impl/controller/symmetric/commercialenergysaver/Ess.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.commercialenergysaver; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final WriteChannel setWorkState; - public final ReadChannel systemState; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - setWorkState = ess.setWorkState().required(); - systemState = ess.systemState().required(); - } - -} diff --git a/edge/src/io/openems/impl/controller/symmetric/commercialworkstate/Ess.java b/edge/src/io/openems/impl/controller/symmetric/commercialworkstate/Ess.java index 5506471d2f2..16b14fcecec 100644 --- a/edge/src/io/openems/impl/controller/symmetric/commercialworkstate/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/commercialworkstate/Ess.java @@ -1,45 +1,41 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.commercialworkstate; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final WriteChannel setWorkState; - public final ReadChannel systemState; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - setWorkState = ess.setWorkState().required(); - systemState = ess.systemState().required(); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.commercialworkstate; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final WriteChannel setWorkState; + public final ReadChannel systemState; + + public Ess(SymmetricEssNature ess) { + super(ess); + setWorkState = ess.setWorkState().required(); + systemState = ess.systemState().required(); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java index d85104d57eb..6a8c8113a4b 100644 --- a/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java +++ b/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java @@ -25,7 +25,7 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.power.PowerException; import io.openems.impl.controller.symmetric.balancingcosphi.Ess; @ThingInfo(title = "Ess Cos-Phi (Symmetric)", description = "Keeps the Ess at a given cos-phi. For symmetric Ess.") @@ -61,17 +61,12 @@ public CosPhiController(String thingId) { public void run() { try { Ess ess = this.ess.value(); - if (ess.setActivePower.peekWrite().isPresent()) { - long expectedActivePower = ess.setActivePower.peekWrite().get();//40 - long q = ControllerUtils.calculateReactivePower(expectedActivePower, cosPhi.value(), capacitive.value()); - ess.power.setReactivePower(q); - ess.power.writePower(); - log.info("Set ReactivePower [" + ess.power.getReactivePower() + "]"); - } else { - log.error(ess.id() + " no ActivePower is Set."); - } + ess.limit.setCosPhi(cosPhi.valueOptional().orElse(null), capacitive.valueOptional().orElse(null), 0L, 0L); + ess.power.applyLimitation(ess.limit); } catch (InvalidValueException e) { log.error("No ess found.", e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java b/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java index fe05ee818c0..ac5268e4928 100644 --- a/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java @@ -1,51 +1,48 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.cosphi; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final String id; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower(); - setReactivePower = ess.setReactivePower(); - id = ess.id(); - allowedCharge = ess.allowedCharge(); - allowedDischarge = ess.allowedDischarge(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.cosphi; + +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.SymmetricEssNature; +import io.openems.core.utilities.power.CosPhiLineLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final String id; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final SymmetricPower power; + public final CosPhiLineLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + id = ess.id(); + allowedCharge = ess.allowedCharge(); + allowedDischarge = ess.allowedDischarge(); + power = ess.getPower(); + limit = new CosPhiLineLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java index f89b6ab2fc2..553179797ed 100644 --- a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java +++ b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java @@ -20,19 +20,21 @@ *******************************************************************************/ package io.openems.impl.controller.symmetric.cosphicharacteristic; -import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.TreeMap; +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.ControllerUtils; -import io.openems.core.utilities.Point; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Cos-Phi Characteristics (Symmetric)") -public class CosPhiCharacteristicController extends Controller { +public class CosPhiCharacteristicController extends Controller implements ChannelChangeListener{ /* * Constructors @@ -49,27 +51,11 @@ public CosPhiCharacteristicController(String id) { * Config */ @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel<>("ess", this); + public ConfigChannel ess = new ConfigChannel("ess", this).addChangeListener(this); - @ChannelInfo(title = "Cos-Phi characteristic", description = "The points of the characteristic (x = signed activePower, y = cosPhi IEEE Power Factor Sign Convention ).", type = Long[].class, isArray = true) - public ConfigChannel> cosPhiPoints = new ConfigChannel>("cosPhiPoints", this) - .addChangeListener((channel, newValue, oldValue) -> { - List points = new ArrayList<>(); - if (newValue.isPresent()) { - @SuppressWarnings("unchecked") List cosPhiPoints = (List) newValue.get(); - for (Long[] arr : cosPhiPoints) { - points.add(new Point(arr[0], arr[1])); - } - } else { - log.error("found no cosPhiPoints!"); - } - cosPhiCharacteristic = points; - }); - - /* - * Fields - */ - public List cosPhiCharacteristic; + @ChannelInfo(title = "Cos-Phi characteristic", description = "The points of the characteristic (x = signed activePower, y = cosPhi IEEE Power Factor Sign Convention ).", type = Double[].class, isArray = true) + public ConfigChannel> cosPhiPoints = new ConfigChannel>("cosPhiPoints", this) + .addChangeListener(this); /* * Methods @@ -77,24 +63,22 @@ public CosPhiCharacteristicController(String id) { @Override public void run() { try { - if (ess.value().setActivePower.peekWrite().isPresent()) { - double pRatio = (double) ess.value().setActivePower.peekWrite().get() - / (double) ess.value().nominalPower.value() * 100; - double cosPhi = ControllerUtils.getValueOfLine(cosPhiCharacteristic, pRatio) / 100; - boolean capacitive = false; - if((pRatio<0 && cosPhi<0)||(pRatio>0 && cosPhi>0)) { - capacitive = true; + ess.value().power.applyLimitation(ess.value().limit); + } catch (InvalidValueException | PowerException e) { + log.error("Failed to set power limitation!",e); + } + } + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + if(ess.isValuePresent()) { + TreeMap points = new TreeMap<>(); + if(cosPhiPoints.isValuePresent()) { + for(Double[] point : cosPhiPoints.getValue()) { + points.put(point[0].longValue(), point[1]); } - ess.value().power.setReactivePower( - ControllerUtils.calculateReactivePower(ess.value().setActivePower.peekWrite().get(), cosPhi,capacitive)); - ess.value().power.writePower(); - log.info("Set reactive power [{}] to get cosPhi [{}]", - new Object[] { ess.value().power.getReactivePower(), cosPhi }); - } else { - log.error(ess.id() + " no ActivePower is Set."); } - } catch (InvalidValueException e) { - log.error("No ess found.", e); + ess.getValue().limit.setCosPhi(0L, 0L, points); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java index bbb90395ba1..dd7a1bdcfff 100644 --- a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java @@ -1,53 +1,50 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.cosphicharacteristic; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final String id; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final SymmetricPower power; - public final ReadChannel nominalPower; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower(); - setReactivePower = ess.setReactivePower(); - id = ess.id(); - allowedCharge = ess.allowedCharge(); - allowedDischarge = ess.allowedDischarge(); - nominalPower = ess.maxNominalPower(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.cosphicharacteristic; + +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.SymmetricEssNature; +import io.openems.core.utilities.power.CosPhiLineCharacteristicLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final String id; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final SymmetricPower power; + public final ReadChannel nominalPower; + public final CosPhiLineCharacteristicLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + id = ess.id(); + allowedCharge = ess.allowedCharge(); + allowedDischarge = ess.allowedDischarge(); + nominalPower = ess.maxNominalPower(); + power = ess.getPower(); + limit = new CosPhiLineCharacteristicLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java index 45d18d5f58c..c56ead30190 100644 --- a/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java @@ -1,41 +1,46 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.fixvalue; - -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final SymmetricPower power; - public final String id; - - public Ess(SymmetricEssNature ess) { - super(ess); - id = ess.id(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.fixvalue; + +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.QEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final SymmetricPower power; + public final PEqualLimitation activePowerLimit; + public final QEqualLimitation reactivePowerLimit; + public final String id; + + public Ess(SymmetricEssNature ess) { + super(ess); + id = ess.id(); + power = ess.getPower(); + activePowerLimit = new PEqualLimitation(power); + reactivePowerLimit = new QEqualLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java new file mode 100644 index 00000000000..e0c901171b7 --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * 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.symmetric.fixvalue; + +import java.util.List; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Fixed active and reactive power (Symmetric)", description = "Charges or discharges the battery with a predefined, fixed power. For symmetric Ess.") +public class FixValueActivePowerController extends Controller { + + /* + * Constructors + */ + public FixValueActivePowerController() { + super(); + } + + public FixValueActivePowerController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) + public ConfigChannel> esss = new ConfigChannel>("esss", this); + + @ChannelInfo(title = "ActivePower", description = "The active power to set for each Ess.", type = Long.class) + public ConfigChannel p = new ConfigChannel("p", this); + + /* + * Methods + */ + @Override + public void run() { + try { + for (Ess ess : esss.value()) { + ess.activePowerLimit.setP(p.valueOptional().orElse(null)); + ess.power.applyLimitation(ess.activePowerLimit); + } + } catch (InvalidValueException e) { + log.error("No ess found.", e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueController.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java similarity index 74% rename from edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueController.java rename to edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java index 0a0a28a5af4..e4271f169c3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueController.java +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java @@ -27,18 +27,19 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Fixed active and reactive power (Symmetric)", description = "Charges or discharges the battery with a predefined, fixed power. For symmetric Ess.") -public class FixValueController extends Controller { +public class FixValueReactivePowerController extends Controller { /* * Constructors */ - public FixValueController() { + public FixValueReactivePowerController() { super(); } - public FixValueController(String thingId) { + public FixValueReactivePowerController(String thingId) { super(thingId); } @@ -48,11 +49,8 @@ public FixValueController(String thingId) { @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) public ConfigChannel> esss = new ConfigChannel>("esss", this); - @ChannelInfo(title = "ActivePower", description = "The active power to set for each Ess.", type = Integer.class, isOptional = true) - public ConfigChannel p = new ConfigChannel("p", this); - - @ChannelInfo(title = "ReactivePower", description = "The reactive power to set for each Ess.", type = Integer.class, isOptional = true) - public ConfigChannel q = new ConfigChannel("q", this); + @ChannelInfo(title = "ReactivePower", description = "The reactive power to set for each Ess.", type = Long.class, isOptional = true) + public ConfigChannel q = new ConfigChannel("q", this); /* * Methods @@ -61,16 +59,13 @@ public FixValueController(String thingId) { public void run() { try { for (Ess ess : esss.value()) { - if (p.valueOptional().isPresent()) { - ess.power.setActivePower(p.value()); - } - if (q.valueOptional().isPresent()) { - ess.power.setReactivePower(q.value()); - } - ess.power.writePower(); + ess.reactivePowerLimit.setQ(q.valueOptional().orElse(null)); + ess.power.applyLimitation(ess.reactivePowerLimit); } } catch (InvalidValueException e) { log.error("No ess found.", e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java index d9289e1a36f..cb77e76d538 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java @@ -1,57 +1,59 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.powerbyfrequency; - -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.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final ReadChannel soc; - public final ReadChannel activePower; - public final ReadChannel reactivePower; - public final ReadChannel systemState; - public final ReadChannel maxNominalPower; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - minSoc = ess.minSoc().required(); - - soc = ess.soc().required(); - activePower = ess.activePower().required(); - systemState = ess.systemState().required(); - reactivePower = ess.reactivePower().required(); - maxNominalPower = ess.maxNominalPower().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - - public long useableSoc() throws InvalidValueException { - return soc.value() - minSoc.value(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.powerbyfrequency; + +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.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel activePower; + public final ReadChannel reactivePower; + public final ReadChannel systemState; + public final ReadChannel maxNominalPower; + public final SymmetricPower power; + public final PEqualLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + minSoc = ess.minSoc().required(); + + soc = ess.soc().required(); + activePower = ess.activePower().required(); + systemState = ess.systemState().required(); + reactivePower = ess.reactivePower().required(); + maxNominalPower = ess.maxNominalPower().required(); + power = ess.getPower(); + limit = new PEqualLimitation(power); + } + + public long useableSoc() throws InvalidValueException { + return soc.value() - minSoc.value(); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java index c9d4959bea1..289017d0a5f 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java @@ -1,91 +1,93 @@ -/******************************************************************************* - * 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.symmetric.powerbyfrequency; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; - -@ThingInfo(title = "Power by frequency (Symmetric)", description = "Tries to keep the grid meter at a given frequency. For symmetric Ess.") -public class PowerByFrequencyController extends Controller { - - /* - * Constructors - */ - public PowerByFrequencyController() { - super(); - } - - public PowerByFrequencyController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel<>("ess", this); - - @ChannelInfo(title = "Meter", description = "The meter for the frequency meassurement.", type = Meter.class) - public final ConfigChannel meter = new ConfigChannel<>("meter", this); - - @ChannelInfo(title = "Low SOC-Limit", description = "The low soc limit. Below this limit the Ess will charge with more power by the same frequency.", type = Integer.class, defaultValue = "30") - public final ConfigChannel lowSocLimit = new ConfigChannel("lowSocLimit", this); - - @ChannelInfo(title = "High SOC-Limit", description = "The upper soc limit. Above this limit the Ess will discharge with more power by the same frequency.", type = Integer.class, defaultValue = "70") - public final ConfigChannel highSocLimit = new ConfigChannel("highSocLimit", this); - - /* - * Methods - */ - @Override - public void run() { - try { - Ess ess = this.ess.value(); - Meter meter = this.meter.value(); - // Calculate required sum values - long activePower = 0L; - if (meter.frequency.value() >= 49990 && meter.frequency.value() <= 50010) { - // charge if SOC isn't in the expected range - if ((ess.soc.value() > highSocLimit.value() && meter.frequency.value() < 50000) - || (ess.soc.value() < lowSocLimit.value() && meter.frequency.value() > 50000)) { - activePower = (long) (ess.maxNominalPower.value() * (300.0 - 0.006 * meter.frequency.value())); - } - } else { - // calculate minimal Power for Frequency - activePower = (long) ((double) ess.maxNominalPower.value() * (250.0 - meter.frequency.value() / 200.0)); - if ((meter.frequency.value() < 50000 && ess.soc.value() > highSocLimit.value()) - || (meter.frequency.value() > 50000 && ess.soc.value() < lowSocLimit.value())) { - // calculate maximal Power for frequency - activePower = (long) (ess.maxNominalPower.value() * (300 - 0.006 * meter.frequency.value())); - } - } - ess.power.setActivePower(activePower); - ess.power.writePower(); - log.info(ess.id() + " Set ActivePower [" + ess.power.getActivePower() + "]"); - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } - -} +/******************************************************************************* + * 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.symmetric.powerbyfrequency; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Power by frequency (Symmetric)", description = "Tries to keep the grid meter at a given frequency. For symmetric Ess.") +public class PowerByFrequencyController extends Controller { + + /* + * Constructors + */ + public PowerByFrequencyController() { + super(); + } + + public PowerByFrequencyController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess device.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel<>("ess", this); + + @ChannelInfo(title = "Meter", description = "The meter for the frequency meassurement.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel<>("meter", this); + + @ChannelInfo(title = "Low SOC-Limit", description = "The low soc limit. Below this limit the Ess will charge with more power by the same frequency.", type = Integer.class, defaultValue = "30") + public final ConfigChannel lowSocLimit = new ConfigChannel("lowSocLimit", this); + + @ChannelInfo(title = "High SOC-Limit", description = "The upper soc limit. Above this limit the Ess will discharge with more power by the same frequency.", type = Integer.class, defaultValue = "70") + public final ConfigChannel highSocLimit = new ConfigChannel("highSocLimit", this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + Meter meter = this.meter.value(); + // Calculate required sum values + long activePower = 0L; + if (meter.frequency.value() >= 49990 && meter.frequency.value() <= 50010) { + // charge if SOC isn't in the expected range + if ((ess.soc.value() > highSocLimit.value() && meter.frequency.value() < 50000) + || (ess.soc.value() < lowSocLimit.value() && meter.frequency.value() > 50000)) { + activePower = (long) (ess.maxNominalPower.value() * (300.0 - 0.006 * meter.frequency.value())); + } + } else { + // calculate minimal Power for Frequency + activePower = (long) ((double) ess.maxNominalPower.value() * (250.0 - meter.frequency.value() / 200.0)); + if ((meter.frequency.value() < 50000 && ess.soc.value() > highSocLimit.value()) + || (meter.frequency.value() > 50000 && ess.soc.value() < lowSocLimit.value())) { + // calculate maximal Power for frequency + activePower = (long) (ess.maxNominalPower.value() * (300 - 0.006 * meter.frequency.value())); + } + } + ess.limit.setP(activePower); + ess.power.applyLimitation(ess.limit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/PowerLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java similarity index 51% rename from edge/src/io/openems/impl/controller/symmetric/powerlimitation/PowerLimitationController.java rename to edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java index df36f48ed98..ebd76d75c44 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/PowerLimitationController.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java @@ -1,101 +1,84 @@ -/******************************************************************************* - * 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.symmetric.powerlimitation; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; - -@ThingInfo(title = "Power limitation (Symmetric)", description = "Limits the active and reactive power of the Ess. For symmetric Ess.") -public class PowerLimitationController extends Controller { - - /* - * Constructors - */ - public PowerLimitationController() { - super(); - } - - public PowerLimitationController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Min-Charge ActivePower", description = "The minimum allowed active power for discharge. Value is negative.", type = Long.class) - public ConfigChannel pMin = new ConfigChannel("pMin", this); - - @ChannelInfo(title = "Max-Charge ActivePower", description = "The maximum allowed active power for discharge. Value is positive.", type = Long.class) - public ConfigChannel pMax = new ConfigChannel("pMax", this); - - @ChannelInfo(title = "Min-Charge ReactivePower", description = "The minimum allowed reactive power for discharge. Value is negative.", type = Long.class) - public ConfigChannel qMin = new ConfigChannel("qMin", this); - - @ChannelInfo(title = "Max-Charge ReactivePower", description = "The maximum allowed reactive power for discharge. Value is positive.", type = Long.class) - public ConfigChannel qMax = new ConfigChannel("qMax", this); - - /* - * Methods - */ - @Override - public void run() { - try { - try { - if (pMax.value() < ess.value().setActivePower.writeMax().orElse(Long.MAX_VALUE)) { - ess.value().setActivePower.pushWriteMax(pMax.value()); - } - } catch (WriteChannelException | InvalidValueException e) { - log.error("Failed to write Max P value for [" + ess.value().id + "]: " + e.getMessage()); - } - try { - if (pMin.value() > ess.value().setActivePower.writeMin().orElse(Long.MIN_VALUE)) { - ess.value().setActivePower.pushWriteMin(pMin.value()); - } - } catch (WriteChannelException | InvalidValueException e) { - log.error("Failed to write Min P value for [" + ess.value().id + "]: " + e.getMessage()); - } - try { - if (qMin.value() > ess.value().setReactivePower.writeMin().orElse(Long.MIN_VALUE)) { - ess.value().setReactivePower.pushWriteMin(qMin.value()); - } - } catch (WriteChannelException | InvalidValueException e) { - log.error("Failed to write Min Q value for [" + ess.value().id + "]: " + e.getMessage()); - } - try { - if (qMax.value() < ess.value().setReactivePower.writeMax().orElse(Long.MAX_VALUE)) { - ess.value().setReactivePower.pushWriteMax(qMax.value()); - } - } catch (WriteChannelException | InvalidValueException e) { - log.error("Failed to write Max Q value for [" + ess.value().id + "]: " + e.getMessage()); - } - } catch (InvalidValueException e) { - log.error("No ess found.", e); - } - } - -} +/******************************************************************************* + * 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.symmetric.powerlimitation; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Power limitation (Symmetric)", description = "Limits the active and reactive power of the Ess. For symmetric Ess.") +public class ActivePowerLimitationController extends Controller { + + /* + * Constructors + */ + public ActivePowerLimitationController() { + super(); + } + + public ActivePowerLimitationController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Min-Charge ActivePower", description = "The minimum allowed active power for discharge. Value is negative.", type = Long.class, isOptional = true) + public ConfigChannel pMin = new ConfigChannel("pMin", this); + + @ChannelInfo(title = "Max-Charge ActivePower", description = "The maximum allowed active power for discharge. Value is positive.", type = Long.class, isOptional = true) + public ConfigChannel pMax = new ConfigChannel("pMax", this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + if (pMin.isValuePresent()) { + ess.minActivePowerLimit.setP(pMin.valueOptional().orElse(null)); + try { + ess.power.applyLimitation(ess.minActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to write Min P",e); + } + } + if(pMax.isValuePresent()) { + ess.maxActivePowerLimit.setP(pMax.valueOptional().orElse(null)); + try { + ess.power.applyLimitation(ess.maxActivePowerLimit); + } catch (PowerException e) { + log.error("Failed to write Max P",e); + } + } + } catch (InvalidValueException e) { + log.error("No ess found.", e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java index 6316e93eb19..2bb837439f3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java @@ -1,42 +1,52 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.powerlimitation; - -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final String id; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower(); - setReactivePower = ess.setReactivePower(); - id = ess.id(); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.powerlimitation; + +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.QGreaterEqualLimitation; +import io.openems.core.utilities.power.QSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final String id; + public final SymmetricPower power; + public final PGreaterEqualLimitation minActivePowerLimit; + public final PSmallerEqualLimitation maxActivePowerLimit; + public final QGreaterEqualLimitation minReactivePowerLimit; + public final QSmallerEqualLimitation maxReactivePowerLimit; + + public Ess(SymmetricEssNature ess) { + super(ess); + id = ess.id(); + this.power = ess.getPower(); + this.minActivePowerLimit = new PGreaterEqualLimitation(power); + this.maxActivePowerLimit = new PSmallerEqualLimitation(power); + this.minReactivePowerLimit = new QGreaterEqualLimitation(power); + this.maxReactivePowerLimit = new QSmallerEqualLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java index 6ba558550da..c3a442469bb 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java @@ -1,51 +1,48 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.powerramp; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel activePower; - public final String id; - public final SymmetricPower power; - public final ReadChannel gridMode; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower(); - setReactivePower = ess.setReactivePower(); - id = ess.id(); - activePower = ess.activePower().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - this.gridMode = ess.gridMode(); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.powerramp; + +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.SymmetricEssNature; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel activePower; + public final String id; + public final SymmetricPower power; + public final ReadChannel gridMode; + public final PEqualLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + id = ess.id(); + activePower = ess.activePower().required(); + this.gridMode = ess.gridMode(); + this.power = ess.getPower(); + this.limit = new PEqualLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java b/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java index 0c679b513fe..1952ae331c9 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java @@ -28,7 +28,7 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.SymmetricPower; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Power ramp (Symmetric)", description = "Follows a power ramp. For symmetric Ess.") public class PowerRampController extends Controller { @@ -50,8 +50,8 @@ public PowerRampController(String thingId) { @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class, isArray = true) public ConfigChannel> esss = new ConfigChannel>("esss", this); - @ChannelInfo(title = "Max-ActivePower", description = "The limit where the powerRamp stops. (pos/neg)", type = Integer.class) - public ConfigChannel pMax = new ConfigChannel("pMax", this); + @ChannelInfo(title = "Max-ActivePower", description = "The limit where the powerRamp stops. (pos/neg)", type = Long.class) + public ConfigChannel pMax = new ConfigChannel("pMax", this); @ChannelInfo(title = "Step", description = "Step to increase power.", type = Integer.class) public ConfigChannel pStep = new ConfigChannel("pStep", this); @@ -77,23 +77,22 @@ public void run() { && ess.gridMode.labelOptional().get().equals(EssNature.OFF_GRID)) { lastPower = 0; } - SymmetricPower power = ess.power; + // SymmetricPower power = ess.power; if (lastSet + sleep.value() < System.currentTimeMillis()) { if (Math.abs(lastPower + pStep.value()) <= Math.abs(pMax.value())) { - power.setActivePower(lastPower + pStep.value()); + ess.limit.setP(lastPower + pStep.value()); } else { - power.setActivePower(pMax.value()); + ess.limit.setP(pMax.value()); } lastSet = System.currentTimeMillis(); } else { - power.setActivePower(lastPower); + ess.limit.setP(lastPower); } - power.writePower(); - lastPower = power.getActivePower(); - log.info("Set ActivePower [" + power.getActivePower() + "] Set ReactivePower [" - + power.getReactivePower() + "]"); + ess.power.applyLimitation(ess.limit); } catch (InvalidValueException e) { log.error("Failed to write fixed P/Q value for Ess " + ess.id, e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } } } catch (InvalidValueException e) { diff --git a/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java deleted file mode 100644 index a5d5f357979..00000000000 --- a/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java +++ /dev/null @@ -1,219 +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.symmetric.refuavoidtotaldischarge; - -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.WriteChannelException; -import io.openems.core.utilities.hysteresis.Hysteresis; - -@ThingInfo(title = "REFU: Avoid Total Discharge") -public class AvoidTotalDischargeController extends Controller { - - @ChannelInfo(title = "Storage, where total discharge should be avoided. For excample to reserve load for the Off-Grid power supply.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel("ess", this); - @ChannelInfo(title = "Delay, to allow Power after start", type = Long.class) - public final ConfigChannel powerDelay = new ConfigChannel<>("powerDelay", this); - @ChannelInfo(title = "Step to increase the allowed power after start delay.", type = Long.class) - public final ConfigChannel powerStep = new ConfigChannel<>("powerStep", this); - @ChannelInfo(title = "Soc limit to stop chargePower.", type = Long.class) - public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this) - .addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (maxSoc.valueOptional().isPresent() && socHysteresis.valueOptional().isPresent()) { - try { - socMaxHysteresis = new Hysteresis(maxSoc.value() - socHysteresis.value(), maxSoc.value()); - } catch (InvalidValueException e) {} - } - } - }); - @ChannelInfo(title = "Soc hysteresis for max Soc limit.", type = Long.class) - public final ConfigChannel socHysteresis = new ConfigChannel("socHysteresis", this) - .addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (maxSoc.valueOptional().isPresent() && socHysteresis.valueOptional().isPresent()) { - try { - socMaxHysteresis = new Hysteresis(maxSoc.value() - socHysteresis.value(), maxSoc.value()); - } catch (InvalidValueException e) {} - } - } - }); - private boolean isStart = false; - private long timeStartOccured = System.currentTimeMillis(); - private long lastPower = 0L; - private Hysteresis socMaxHysteresis; - - public AvoidTotalDischargeController() { - super(); - } - - public AvoidTotalDischargeController(String thingId) { - super(thingId); - } - - @Override - public void run() { - try { - Ess ess = this.ess.value(); - - if (ess.systemState.value() == 4) { - if (!isStart) { - timeStartOccured = System.currentTimeMillis(); - isStart = true; - } - } else { - isStart = false; - lastPower = 0L; - } - if (isStart && timeStartOccured + powerDelay.value() <= System.currentTimeMillis()) { - lastPower += powerStep.value(); - if (lastPower > ess.nominalPower.value()) { - lastPower = ess.nominalPower.value(); - } - } - try { - ess.setActivePower.pushWriteMin(lastPower * -1); - } catch (WriteChannelException e) { - // catch out of bounds - } - try { - ess.setActivePower.pushWriteMax(lastPower); - } catch (WriteChannelException e) { - // catch out of bounds - } - try { - ess.setReactivePower.pushWriteMin(lastPower * -1); - } catch (WriteChannelException e) { - // catch out of bounds - } - try { - ess.setReactivePower.pushWriteMax(lastPower); - } catch (WriteChannelException e) { - // catch out of bounds - } - - /* - * Calculate SetActivePower according to MinSoc - */ - - ess.socMinHysteresis.apply(ess.soc.value(), (state, multiplier) -> { - switch (state) { - case ASC: - case DESC: - if (!ess.isChargeSoc) { - try { - long maxPower = 0; - if (!ess.setActivePower.writeMax().isPresent() - || maxPower < ess.setActivePower.writeMax().get()) { - ess.setActivePower.pushWriteMax(maxPower); - } - } catch (WriteChannelException e) { - log.error(ess.id() + "Failed to set Max allowed power.", e); - } - } - break; - case BELOW: - if (!ess.isChargeSoc) { - try { - if (ess.soc.value() < ess.chargeSoc.value()) { - ess.isChargeSoc = true; - } - } catch (Exception e) { - log.error(e.getMessage()); - } - } - break; - case ABOVE: - ess.isChargeSoc = false; - default: - break; - } - if (ess.isChargeSoc) { - try { - Optional currentMinValue = ess.setActivePower.writeMin(); - if (currentMinValue.isPresent() && currentMinValue.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePower=Max[" + currentMinValue.get() / 5 + "]"); - ess.setActivePower.pushWriteMax(currentMinValue.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePower=Max[-1000 W]"); - ess.setActivePower.pushWriteMax(-1000L); - } - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - }); - - if (socMaxHysteresis != null) { - socMaxHysteresis.apply(ess.soc.value(), (state, multiplier) -> { - switch (state) { - case ABOVE: - try { - ess.setActivePower.pushWriteMin(0L); - } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - break; - case ASC: - try { - ess.setActivePower.pushWriteMin((long) (ess.allowedCharge.value() * multiplier)); - } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - break; - case BELOW: - break; - case DESC: - try { - ess.setActivePower.pushWriteMin(0L); - } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - break; - default: - break; - } - }); - } - } catch (InvalidValueException e) { - log.error(e.getMessage()); - } - } -} diff --git a/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/Ess.java deleted file mode 100644 index d3d12a1bcf3..00000000000 --- a/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/Ess.java +++ /dev/null @@ -1,76 +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.symmetric.refuavoidtotaldischarge; - -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.core.utilities.hysteresis.Hysteresis; -import io.openems.impl.device.refu.RefuEss; - -@IsThingMap(type = RefuEss.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel soc; - public final ReadChannel systemState; - public int maxPowerPercent = 100; - public boolean isChargeSoc = false; - public final ReadChannel allowedDischarge; - public final ReadChannel chargeSoc; - public Hysteresis socMinHysteresis; - public final ReadChannel nominalPower; - public final ReadChannel batteryVoltage; - public final ReadChannel allowedCharge; - - public Ess(RefuEss ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - systemState = ess.systemState().required(); - soc = ess.soc().required(); - minSoc = ess.minSoc().required(); - allowedDischarge = ess.allowedDischarge().required(); - chargeSoc = ess.chargeSoc().required(); - setReactivePower = ess.setReactivePower().required(); - nominalPower = ess.maxNominalPower().required(); - batteryVoltage = ess.batteryVoltage.required(); - allowedCharge = ess.allowedDischarge().required(); - minSoc.addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (newValue.isPresent()) { - socMinHysteresis = new Hysteresis(((Long) newValue.get()) - 2, ((Long) newValue.get()) + 2); - } - } - }); - if (minSoc.valueOptional().isPresent()) { - socMinHysteresis = new Hysteresis(minSoc.valueOptional().get() - 2, minSoc.valueOptional().get() + 2); - } - } -} diff --git a/edge/src/io/openems/impl/controller/symmetric/refuworkstate/Ess.java b/edge/src/io/openems/impl/controller/symmetric/refuworkstate/Ess.java index da89be79148..6bb678f9238 100644 --- a/edge/src/io/openems/impl/controller/symmetric/refuworkstate/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/refuworkstate/Ess.java @@ -1,47 +1,43 @@ -/******************************************************************************* - * 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.symmetric.refuworkstate; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.impl.device.refu.RefuEss; - -@IsThingMap(type = RefuEss.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final WriteChannel setWorkState; - public final WriteChannel setSystemErrorReset; - public final ReadChannel systemState; - - public Ess(RefuEss ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - setWorkState = ess.setWorkState().required(); - systemState = ess.systemState().required(); - setSystemErrorReset = ess.setSystemErrorReset.required(); - } - -} +/******************************************************************************* + * 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.symmetric.refuworkstate; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.impl.device.refu.RefuEss; + +@IsThingMap(type = RefuEss.class) +public class Ess extends ThingMap { + + public final WriteChannel setWorkState; + public final WriteChannel setSystemErrorReset; + public final ReadChannel systemState; + + public Ess(RefuEss ess) { + super(ess); + setWorkState = ess.setWorkState().required(); + systemState = ess.systemState().required(); + setSystemErrorReset = ess.setSystemErrorReset.required(); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java index 36c4fdfedcb..05750f1cdaa 100644 --- a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java @@ -1,49 +1,57 @@ -/******************************************************************************* - * 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.symmetric.timelinecharge; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel soc; - public final ReadChannel activePower; - public final ReadChannel reactivePower; - public final ReadChannel gridMode; - public final ReadChannel capacity; - public final WriteChannel setActivePower; - - public Ess(SymmetricEssNature ess) { - super(ess); - reactivePower = ess.reactivePower().required(); - soc = ess.soc().required(); - activePower = ess.activePower().required(); - gridMode = ess.gridMode().required(); - this.capacity = ess.capacity().required(); - this.setActivePower = ess.setActivePower().required(); - } - -} +/******************************************************************************* + * 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.symmetric.timelinecharge; + +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.SymmetricEssNature; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SMaxLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel soc; + public final ReadChannel activePower; + public final ReadChannel reactivePower; + public final ReadChannel gridMode; + public final ReadChannel capacity; + public final SymmetricPower power; + public final PSmallerEqualLimitation maxActivePowerlimit; + public final SMaxLimitation maxApparentPower; + public final ReadChannel maxNominalPower; + + public Ess(SymmetricEssNature ess) { + super(ess); + reactivePower = ess.reactivePower().required(); + soc = ess.soc().required(); + activePower = ess.activePower().required(); + gridMode = ess.gridMode().required(); + this.capacity = ess.capacity().required(); + power = ess.getPower(); + maxActivePowerlimit = new PSmallerEqualLimitation(power); + maxApparentPower = new SMaxLimitation(power); + maxNominalPower = ess.maxNominalPower(); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java index 034b2523a99..e37d0dbf9c4 100644 --- a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java @@ -42,11 +42,10 @@ import io.openems.api.exception.ConfigException; import io.openems.api.exception.InvalidValueException; import io.openems.api.exception.ReflectionException; -import io.openems.api.exception.WriteChannelException; import io.openems.common.session.Role; import io.openems.core.utilities.AvgFiFoQueue; -import io.openems.core.utilities.ControllerUtils; import io.openems.core.utilities.JsonUtils; +import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Timeline charge (Symmetric)") public class TimelineChargeController extends Controller { @@ -77,25 +76,32 @@ public TimelineChargeController(String thingId) { @ChannelInfo(title = "Charger", description = "Sets the Chargers connected to the ess.", type = Charger.class, isArray = true) public final ConfigChannel> chargers = new ConfigChannel>("chargers", this); - @ChannelInfo(title = "Monday", description = "Sets the soc limits for monday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Monday", description = "Sets the soc limits for monday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel monday = new ConfigChannel<>("monday", this); - @ChannelInfo(title = "Tuesday", description = "Sets the soc limits for tuesday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Tuesday", description = "Sets the soc limits for tuesday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel tuesday = new ConfigChannel<>("tuesday", this); - @ChannelInfo(title = "Wednesday", description = "Sets the soc limits for wednesday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Wednesday", description = "Sets the soc limits for wednesday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel wednesday = new ConfigChannel<>("wednesday", this); - @ChannelInfo(title = "Thursday", description = "Sets the soc limits for thursday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Thursday", description = "Sets the soc limits for thursday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel thursday = new ConfigChannel<>("thursday", this); - @ChannelInfo(title = "Friday", description = "Sets the soc limits for friday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Friday", description = "Sets the soc limits for friday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel friday = new ConfigChannel<>("friday", this); - @ChannelInfo(title = "Saturday", description = "Sets the soc limits for saturday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Saturday", description = "Sets the soc limits for saturday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel saturday = new ConfigChannel<>("saturday", this); - @ChannelInfo(title = "Sunday", description = "Sets the soc limits for sunday.", type = JsonArray.class, writeRoles = { Role.OWNER }) + @ChannelInfo(title = "Sunday", description = "Sets the soc limits for sunday.", type = JsonArray.class, writeRoles = { + Role.OWNER }) public ConfigChannel sunday = new ConfigChannel<>("sunday", this); /* @@ -113,152 +119,116 @@ public enum State { */ @Override public void run() { - // Check if all parameters are available - Ess ess; - long thisAllowedApparent; - long meterApparentPower; - Set chargers; - long essCapacity; - long essSoc; try { - ess = this.ess.value(); - thisAllowedApparent = this.allowedApparent.value(); - meterApparentPower = meter.value().apparentPower.value(); - chargers = this.chargers.value(); - essCapacity = ess.capacity.value(); - essSoc = ess.soc.value(); - } catch (InvalidValueException | NullPointerException e) { - log.error("TimelineChargeController error: " + e.getMessage()); - return; - } - // start controller logic - if (ess.gridMode.labelOptional().equals(Optional.of(EssNature.ON_GRID))) { - long allowedApparentCharge = thisAllowedApparent - meterApparentPower; - allowedApparentCharge += ControllerUtils.calculateApparentPower( - ess.activePower.valueOptional().orElse(0L), ess.reactivePower.valueOptional().orElse(0L)); - // remove 10% for tolerance - allowedApparentCharge *= 0.9; - // limit activePower to apparent - try { - ess.setActivePower.pushWriteMin(allowedApparentCharge * -1); - } catch (WriteChannelException e) { - log.warn("Failed to set writeMin to " + (allowedApparentCharge * -1)); - } - long chargerPower = 0L; - for (Charger c : chargers) { + // Check if all parameters are available + Ess ess = this.ess.value(); + Meter meter = this.meter.value(); + Set chargers = this.chargers.value(); + long essCapacity = ess.capacity.value(); + long essSoc = ess.soc.value(); + + // start controller logic + if (ess.gridMode.labelOptional().equals(Optional.of(EssNature.ON_GRID))) { + long pNull = (meter.activePower.value() - ess.activePower.value())*-1; + long qNull = (meter.reactivePower.value() - ess.reactivePower.value())*-1; + ess.maxApparentPower.setSMax(allowedApparent.value(), pNull, qNull); try { - chargerPower += c.power.value(); - } catch (InvalidValueException e) { - log.error("TimelineChargeController error: Unable to read power from Charger [" + c.id() + "]: " + e.getMessage()); + ess.power.applyLimitation(ess.maxApparentPower); + } catch (PowerException e1) { + log.error("Failed to set Power!",e1); } - } - floatingChargerPower.add(chargerPower); - SocPoint socPoint = getSoc(); - double requiredEnergy = (essCapacity / 100.0 * socPoint.getSoc()) - - (essCapacity / 100.0 * essSoc); - long requiredTimeCharger = (long) (requiredEnergy / floatingChargerPower.avg() * 3600.0); - // limit time to one day - if(requiredTimeCharger > 60*60*24) { - requiredTimeCharger = 60*60*24; - } - long requiredTimeGrid = (long) (requiredEnergy / (floatingChargerPower.avg() + allowedApparentCharge) - * 3600.0); - // limit time to one day - if(requiredTimeGrid > 60*60*24) { - requiredTimeGrid = 60*60*24; - } - log.info("RequiredTimeCharger: " + requiredTimeCharger + ", RequiredTimeGrid: " + requiredTimeGrid); - if (floatingChargerPower.avg() >= 1000 - && !LocalDateTime.now().plusSeconds(requiredTimeCharger).isBefore(socPoint.getTime()) - && LocalDateTime.now().plusSeconds(requiredTimeGrid).isBefore(socPoint.getTime())) { - // Prevent discharge -> load with Pv - try { - ess.setActivePower.pushWriteMax(0L); - } catch (WriteChannelException e) { - log.error("Failed to write Channel [" + ess.setActivePower.address() + "]: " + e.getMessage()); - } - } else if (requiredTimeGrid > 0 - && !LocalDateTime.now().plusSeconds(requiredTimeGrid).isBefore(socPoint.getTime()) - && socPoint.getTime().isAfter(LocalDateTime.now())) { - // Load with grid + pv - long maxPower = allowedApparentCharge * -1; - if (ess.setActivePower.writeMin().isPresent() && ess.setActivePower.writeMin().get() > maxPower) { - maxPower = ess.setActivePower.writeMin().get(); + long chargerPower = 0L; + for (Charger c : chargers) { + try { + chargerPower += c.power.value(); + } catch (InvalidValueException e) { + log.error("TimelineChargeController error: Unable to read power from Charger [" + c.id() + "]: " + e.getMessage()); + } } - try { - ess.setActivePower.pushWriteMax(maxPower); - } catch (WriteChannelException e) { - log.error("Failed to write Channel [" + ess.setActivePower.address() + "]: " + e.getMessage()); + floatingChargerPower.add(chargerPower); + SocPoint socPoint = getSoc(); + double requiredEnergy = (essCapacity / 100.0 * socPoint.getSoc()) + - (essCapacity / 100.0 * essSoc); + long requiredTimeCharger = (long) (requiredEnergy / (floatingChargerPower.avg() * 3600.0)); + // limit time to one day + if(requiredTimeCharger > 60*60*24) { + requiredTimeCharger = 60*60*24; } - } else { - // soc point in the past -> Hold load - int minSoc = getCurrentSoc().getSoc(); - int chargeSoc = minSoc - 5; - if (chargeSoc <= 1) { - chargeSoc = 1; + long requiredTimeGrid = (long) (requiredEnergy / ((floatingChargerPower.avg() + (ess.power.getMinP().orElse(0L)+ess.activePower.value())*-1) + * 3600.0)); + // limit time to one day + if(requiredTimeGrid > 60*60*24) { + requiredTimeGrid = 60*60*24; } - switch (currentState) { - case CHARGESOC: - if (essSoc > minSoc) { - currentState = State.MINSOC; - } else { - try { - Optional currentMinValue = ess.setActivePower.writeMin(); - if (currentMinValue.isPresent() && currentMinValue.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePower=Max[" + currentMinValue.get() / 5 + "]"); - ess.setActivePower.pushWriteMax(currentMinValue.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePower=Max[-1000 W]"); - ess.setActivePower.pushWriteMax(-1000L); + log.info("RequiredTimeCharger: " + requiredTimeCharger + ", RequiredTimeGrid: " + requiredTimeGrid); + if (floatingChargerPower.avg() >= 1000 + && !LocalDateTime.now().plusSeconds(requiredTimeCharger).isBefore(socPoint.getTime()) + && LocalDateTime.now().plusSeconds(requiredTimeGrid).isBefore(socPoint.getTime())) { + // Prevent discharge -> load with Pv + ess.maxActivePowerlimit.setP(0L); + try { + ess.power.applyLimitation(ess.maxActivePowerlimit); + } catch (PowerException e) { + log.error("Failed to set Power [" + ess.id() + "]: ",e); + } + } else if (requiredTimeGrid > 0 + && !LocalDateTime.now().plusSeconds(requiredTimeGrid).isBefore(socPoint.getTime()) + && socPoint.getTime().isAfter(LocalDateTime.now())) { + // Load with grid + pv + ess.maxActivePowerlimit.setP(ess.power.getMinP().get()); + try { + ess.power.applyLimitation(ess.maxActivePowerlimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } else { + // soc point in the past -> Hold load + int minSoc = getCurrentSoc().getSoc(); + int chargeSoc = minSoc - 5; + if (chargeSoc <= 1) { + chargeSoc = 1; + } + switch (currentState) { + case CHARGESOC: + if (essSoc > minSoc) { + currentState = State.MINSOC; + } else { + try { + ess.maxActivePowerlimit.setP(ess.maxNominalPower.valueOptional().orElse(-1000L)); + ess.power.applyLimitation(ess.maxActivePowerlimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } - } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); } - } - break; - case MINSOC: - if (essSoc < chargeSoc) { - currentState = State.CHARGESOC; - } else if (essSoc >= minSoc + 5) { - currentState = State.NORMAL; - } else { - try { - long maxPower = 0; - if (!ess.setActivePower.writeMax().isPresent() - || maxPower < ess.setActivePower.writeMax().get()) { - ess.setActivePower.pushWriteMax(maxPower); + break; + case MINSOC: + if (essSoc < chargeSoc) { + currentState = State.CHARGESOC; + } else if (essSoc >= minSoc + 5) { + currentState = State.NORMAL; + } else { + ess.maxActivePowerlimit.setP(0L); + try { + ess.power.applyLimitation(ess.maxActivePowerlimit); + } catch (PowerException e) { + log.error("Failed to set Power!",e); } - } catch (WriteChannelException e) { - log.error("Failed to set Max allowed power for Ess [" + ess.id() + "]: " + e.getMessage()); } + break; + case NORMAL: + if (essSoc <= minSoc) { + currentState = State.MINSOC; + } + break; } - break; - case NORMAL: - if (essSoc <= minSoc) { - currentState = State.MINSOC; - } - break; } } + } catch (InvalidValueException | NullPointerException e) { + log.error("TimelineChargeController error: " + e.getMessage()); + return; } } - // private Entry getSoc() { - // Entry lastSocPoint = socPoints.floorEntry(LocalDateTime.now()); - // Entry nextSocPoint = socPoints.higherEntry(LocalDateTime.now()); - // if (nextSocPoint != null) { - // return nextSocPoint; - // } - // return lastSocPoint; - // } - - // private int getCurrentSoc() { - // Entry socPoint = socPoints.floorEntry(LocalDateTime.now()); - // return socPoint.getValue(); - // } - private JsonArray getJsonOfDay(DayOfWeek day) throws InvalidValueException { switch (day) { case FRIDAY: @@ -279,30 +249,6 @@ private JsonArray getJsonOfDay(DayOfWeek day) throws InvalidValueException { } } - // private Integer getCurrentSoc() throws InvalidValueException { - // Integer soc = null; - // try { - // JsonArray jHours = getJsonOfDay(LocalDate.now().getDayOfWeek()); - // LocalTime time = LocalTime.now(); - // int count = 1; - // while (soc == null && count < 8) { - // try { - // Entry entry = floorSoc(jHours, time); - // if (entry != null) { - // soc = entry.getValue(); - // } - // } catch (IndexOutOfBoundsException e) { - // time = LocalTime.MAX; - // jHours = getJsonOfDay(LocalDate.now().getDayOfWeek().minus(count)); - // } - // count++; - // } - // } catch (ConfigException e) { - // log.error("failed to find soc", e); - // } - // return soc; - // } - private SocPoint getCurrentSoc() { SocPoint soc = null; JsonArray jHours; diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java similarity index 61% rename from edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java rename to edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java index 42df2c157dd..2f93adc13f7 100644 --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java @@ -1,143 +1,113 @@ -/******************************************************************************* - * 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.symmetric.voltagecharacteristic; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.ControllerUtils; -import io.openems.core.utilities.Point; -import io.openems.core.utilities.SymmetricPower; - -@ThingInfo(title = "Voltage characteristics (Symmetric)") -public class VoltageCharacteristicController extends Controller { - - /* - * Constructors - */ - public VoltageCharacteristicController() { - super(); - initialize(); - } - - public VoltageCharacteristicController(String thingId) { - super(thingId); - initialize(); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public final ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Meter", description = "The meter to measure the Voltage.", type = Meter.class) - public final ConfigChannel meter = new ConfigChannel("meter", this); - - @ChannelInfo(title = "Nominal voltage", description = "The nominal voltage of the grid.", type = Integer.class) - public final ConfigChannel uNenn = new ConfigChannel<>("UNenn", this); - - @ChannelInfo(title = "ActivePower characteristics", description = "Characteristic points for active power.", type = Long[].class, isArray = true) - public final ConfigChannel> pByUCharacteristicPoints = new ConfigChannel<>("pByUCharacteristicPoints", - this); - - @ChannelInfo(title = "ReactivePower characteristics", description = "Characteristic points for reactive power.", type = Long[].class, isArray = true) - public final ConfigChannel> qByUCharacteristicPoints = new ConfigChannel<>("qByUCharacteristicPoints", - this); - - @ChannelInfo(title = "Enable ActivePower", description = "Indicates if active power characteristic is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel activePowerActivated = new ConfigChannel<>("activePowerActivated", this); - - @ChannelInfo(title = "Enable ReactivePower", description = "Indicates if reactive power characteristic is enabled.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel reactivePowerActivated = new ConfigChannel<>("reactivePowerActivated", this); - - /* - * Fields - */ - private List pCharacteristic; - private List qCharacteristic; - - /* - * Methods - */ - private void initialize() { - pByUCharacteristicPoints.addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - try { - List points = new ArrayList<>(); - for (Long[] arr : pByUCharacteristicPoints.value()) { - points.add(new Point(arr[0], arr[1])); - } - pCharacteristic = points; - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - }); - qByUCharacteristicPoints.addChangeListener(new ChannelChangeListener() { - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - try { - List points = new ArrayList<>(); - for (Long[] arr : qByUCharacteristicPoints.value()) { - points.add(new Point(arr[0], arr[1])); - } - qCharacteristic = points; - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - }); - } - - @Override - public void run() { - try { - SymmetricPower power = ess.value().power; - double uRatio = (double) meter.value().voltage.value() / (double) uNenn.value() * 100.0; - long nominalActivePower = ess.value().maxNominalPower.value(); - long nominalReactivePower = ess.value().maxNominalPower.value(); - power.setActivePower( - (long) (nominalActivePower / 100.0 * ControllerUtils.getValueOfLine(pCharacteristic, uRatio))); - power.setReactivePower( - (long) (nominalReactivePower / 100.0 * ControllerUtils.getValueOfLine(qCharacteristic, uRatio))); - power.writePower(); - log.info(ess.id() + " Set ActivePower [" + power.getActivePower() + "], ReactivePower [" - + power.getReactivePower() + "]"); - } catch (InvalidValueException e) { - log.error("Failed to read Value.", e); - } - } - -} +/******************************************************************************* + * 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.symmetric.voltagecharacteristic; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.Point; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Voltage characteristics (Symmetric)") +public class ActivePowerVoltageCharacteristicController extends Controller { + + /* + * Constructors + */ + public ActivePowerVoltageCharacteristicController() { + super(); + initialize(); + } + + public ActivePowerVoltageCharacteristicController(String thingId) { + super(thingId); + initialize(); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Meter", description = "The meter to measure the Voltage.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Nominal voltage", description = "The nominal voltage of the grid.", type = Integer.class) + public final ConfigChannel uNenn = new ConfigChannel<>("UNenn", this); + + @ChannelInfo(title = "ActivePower characteristics", description = "Characteristic points for active power.", type = Long[].class, isArray = true) + public final ConfigChannel> pByUCharacteristicPoints = new ConfigChannel<>("pByUCharacteristicPoints", + this); + + /* + * Fields + */ + private List pCharacteristic; + + /* + * Methods + */ + private void initialize() { + pByUCharacteristicPoints.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + try { + List points = new ArrayList<>(); + for (Long[] arr : pByUCharacteristicPoints.value()) { + points.add(new Point(arr[0], arr[1])); + } + pCharacteristic = points; + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + } + + @Override + public void run() { + try { + Ess ess = this.ess.value(); + double uRatio = (double) meter.value().voltage.value() / (double) uNenn.value() * 100.0; + long nominalActivePower = ess.maxNominalPower.value(); + ess.activePowerLimit.setP( + (long) (nominalActivePower / 100.0 * ControllerUtils.getValueOfLine(pCharacteristic, uRatio))); + ess.power.applyLimitation(ess.activePowerLimit); + } catch (InvalidValueException e) { + log.error("Failed to read Value.", e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java index 6d941d5257f..a93f07f2142 100644 --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java @@ -1,67 +1,66 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.symmetric.voltagecharacteristic; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.SymmetricPower; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final WriteChannel setReactivePower; - public final ReadChannel soc; - public final ReadChannel activePower; - public final ReadChannel reactivePower; - public final ReadChannel allowedCharge; - public final ReadChannel allowedDischarge; - public final ReadChannel systemState; - public final ReadChannel maxNominalPower; - public final SymmetricPower power; - - public Ess(SymmetricEssNature ess) { - super(ess); - minSoc = ess.minSoc().required(); - - setActivePower = ess.setActivePower().required(); - setReactivePower = ess.setReactivePower().required(); - - soc = ess.soc().required(); - activePower = ess.activePower().required(); - allowedCharge = ess.allowedCharge().required(); - allowedDischarge = ess.allowedDischarge().required(); - systemState = ess.systemState().required(); - reactivePower = ess.reactivePower().required(); - maxNominalPower = ess.maxNominalPower().required(); - this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(), - ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required()); - } - - public long useableSoc() throws InvalidValueException { - return soc.value() - minSoc.value(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.voltagecharacteristic; + +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.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PEqualLimitation; +import io.openems.core.utilities.power.QEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final ReadChannel soc; + public final ReadChannel activePower; + public final ReadChannel reactivePower; + public final ReadChannel allowedCharge; + public final ReadChannel allowedDischarge; + public final ReadChannel systemState; + public final ReadChannel maxNominalPower; + public final SymmetricPower power; + public final PEqualLimitation activePowerLimit; + public final QEqualLimitation reactivePowerLimit; + + public Ess(SymmetricEssNature ess) { + super(ess); + minSoc = ess.minSoc().required(); + + soc = ess.soc().required(); + activePower = ess.activePower().required(); + allowedCharge = ess.allowedCharge().required(); + allowedDischarge = ess.allowedDischarge().required(); + systemState = ess.systemState().required(); + reactivePower = ess.reactivePower().required(); + maxNominalPower = ess.maxNominalPower().required(); + power = ess.getPower(); + activePowerLimit = new PEqualLimitation(power); + reactivePowerLimit = new QEqualLimitation(power); + } + + public long useableSoc() throws InvalidValueException { + return soc.value() - minSoc.value(); + } +} diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java new file mode 100644 index 00000000000..066e09a9d48 --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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.symmetric.voltagecharacteristic; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.Point; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Voltage characteristics (Symmetric)") +public class ReactivePowerVoltageCharacteristicController extends Controller { + + /* + * Constructors + */ + public ReactivePowerVoltageCharacteristicController() { + super(); + initialize(); + } + + public ReactivePowerVoltageCharacteristicController(String thingId) { + super(thingId); + initialize(); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Meter", description = "The meter to measure the Voltage.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Nominal voltage", description = "The nominal voltage of the grid.", type = Integer.class) + public final ConfigChannel uNenn = new ConfigChannel<>("UNenn", this); + + @ChannelInfo(title = "ReactivePower characteristics", description = "Characteristic points for reactive power.", type = Long[].class, isArray = true) + public final ConfigChannel> qByUCharacteristicPoints = new ConfigChannel<>("qByUCharacteristicPoints", + this); + + /* + * Fields + */ + private List qCharacteristic; + + /* + * Methods + */ + private void initialize() { + qByUCharacteristicPoints.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + try { + List points = new ArrayList<>(); + for (Long[] arr : qByUCharacteristicPoints.value()) { + points.add(new Point(arr[0], arr[1])); + } + qCharacteristic = points; + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + } + + @Override + public void run() { + try { + Ess ess = this.ess.value(); + double uRatio = (double) meter.value().voltage.value() / (double) uNenn.value() * 100.0; + long nominalReactivePower = ess.maxNominalPower.value(); + ess.reactivePowerLimit.setQ((long) (nominalReactivePower / 100.0 * ControllerUtils.getValueOfLine(qCharacteristic, uRatio))); + ess.power.applyLimitation(ess.reactivePowerLimit); + } catch (InvalidValueException e) { + log.error("Failed to read Value.", e); + } catch (PowerException e) { + log.error("Failed to set Power!",e); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/testwrite/Ess.java b/edge/src/io/openems/impl/controller/testwrite/Ess.java index 96d8646d4fb..40b687f70b8 100644 --- a/edge/src/io/openems/impl/controller/testwrite/Ess.java +++ b/edge/src/io/openems/impl/controller/testwrite/Ess.java @@ -1,39 +1,37 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.testwrite; - -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final WriteChannel setActivePower; - public final WriteChannel setWorkState; - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - setWorkState = ess.setWorkState().required(); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.testwrite; + +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final WriteChannel setWorkState; + + public Ess(SymmetricEssNature ess) { + super(ess); + setWorkState = ess.setWorkState().required(); + } +} From 17800e1af0aaeefff5ae683d6f661d7a3985ac1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Feb 2018 10:51:07 +0100 Subject: [PATCH 033/156] add SymmetricPower class and Limitations --- .../core/utilities/SymmetricPower.java | 305 ------------ .../CosPhiLineCharacteristicLimitation.java | 87 ++++ .../utilities/power/CosPhiLineLimitation.java | 71 +++ .../core/utilities/power/Limitation.java | 39 ++ .../power/LimitationChangedListener.java | 6 + .../utilities/power/MaxCosPhiLimitation.java | 54 +++ .../utilities/power/NoPBetweenLimitation.java | 51 ++ .../utilities/power/NoQBetweenLimitation.java | 51 ++ .../utilities/power/PEqualLimitation.java | 78 ++++ .../power/PGreaterEqualLimitation.java | 51 ++ .../power/PSmallerEqualLimitation.java | 51 ++ .../utilities/power/PowerChangeListener.java | 7 + .../utilities/power/PowerResetListener.java | 9 + .../utilities/power/QEqualLimitation.java | 76 +++ .../power/QGreaterEqualLimitation.java | 51 ++ .../power/QSmallerEqualLimitation.java | 50 ++ .../core/utilities/power/SMaxLimitation.java | 49 ++ .../core/utilities/power/SymmetricPower.java | 437 ++++++------------ .../power/SymmetricPowerClusterImpl.java | 263 +++++++++++ .../utilities/power/SymmetricPowerImpl.java | 128 +++++ 20 files changed, 1317 insertions(+), 597 deletions(-) delete mode 100644 edge/src/io/openems/core/utilities/SymmetricPower.java create mode 100644 edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/Limitation.java create mode 100644 edge/src/io/openems/core/utilities/power/LimitationChangedListener.java create mode 100644 edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/PEqualLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/PowerChangeListener.java create mode 100644 edge/src/io/openems/core/utilities/power/PowerResetListener.java create mode 100644 edge/src/io/openems/core/utilities/power/QEqualLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/SMaxLimitation.java create mode 100644 edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java create mode 100644 edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java diff --git a/edge/src/io/openems/core/utilities/SymmetricPower.java b/edge/src/io/openems/core/utilities/SymmetricPower.java deleted file mode 100644 index e90c4d7d19d..00000000000 --- a/edge/src/io/openems/core/utilities/SymmetricPower.java +++ /dev/null @@ -1,305 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.core.utilities; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.exception.WriteChannelException; - -/** - * Helper class to reduce and set power to the ess - * - * @author matthias.rossmann - * - */ -public class SymmetricPower { - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - - private final ReadChannel allowedDischarge; - private final ReadChannel allowedCharge; - private final ReadChannel allowedApparent; - private final WriteChannel setActivePower; - private final WriteChannel setReactivePower; - private long activePower = 0L; - private long reactivePower = 0L; - private boolean activePowerValid = false; - private boolean reactivePowerValid = false; - - public SymmetricPower(ReadChannel allowedDischarge, ReadChannel allowedCharge, - ReadChannel allowedApparent, WriteChannel setActivePower, WriteChannel setReactivePower) { - super(); - this.allowedDischarge = allowedDischarge; - this.allowedCharge = allowedCharge; - this.allowedApparent = allowedApparent; - this.setActivePower = setActivePower; - this.setReactivePower = setReactivePower; - } - - public void setActivePower(long power) { - this.activePower = power; - this.activePowerValid = true; - } - - public void setReactivePower(long power) { - this.reactivePower = power; - this.reactivePowerValid = true; - } - - public long getActivePower() { - return this.activePower; - } - - public long getReactivePower() { - return this.reactivePower; - } - - /** - * Reduces the active and reactive power to the power limitations - */ - public void reducePower() { - // get Min/Max values - long minActivePower = getMinActivePower(); - long maxActivePower = getMaxActivePower(); - long minReactivePower = getMinReactivePower(); - long maxReactivePower = getMaxReactivePower(); - - // variables for reducedPower - long reducedActivePower = 0L; - long reducedReactivePower = 0L; - - // Check if active power is already set - if (setActivePower.getWriteValue().isPresent()) { - this.activePower = setActivePower.peekWrite().get(); - } - // Check if reactive power is already set - if (setReactivePower.getWriteValue().isPresent()) { - this.reactivePower = setReactivePower.peekWrite().get(); - } - - // calculate cosPhi - double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower); - - if (minActivePower <= activePower && activePower <= maxActivePower && minReactivePower <= reactivePower - && reactivePower <= maxReactivePower) { - // activePower and reactivePower are in allowed value range - // no need to reduce power - reducedActivePower = activePower; - reducedReactivePower = reactivePower; - } else if ((minActivePower > activePower || activePower > maxActivePower) - && (minReactivePower > reactivePower || reactivePower > maxReactivePower)) { - // activePower and reactivePower are out of allowed value range - long reducedActivePower1 = 0L; - long reducedActivePower2 = 0L; - long reducedReactivePower1 = 0L; - long reducedReactivePower2 = 0L; - if (!ControllerUtils.isCharge(activePower, reactivePower)) { - // Discharge - reducedActivePower1 = minActivePower; - reducedReactivePower1 = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi); - reducedReactivePower2 = minReactivePower; - reducedActivePower2 = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower2, - cosPhi); - } else { - // Charge - reducedActivePower1 = maxActivePower; - reducedReactivePower1 = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi); - reducedReactivePower2 = maxReactivePower; - reducedActivePower2 = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower2, - cosPhi); - } - // get largest fitting active and reactive power for min max values - if (ControllerUtils.calculateApparentPower(reducedActivePower1, reducedReactivePower1) > ControllerUtils - .calculateApparentPower(reducedActivePower2, reducedReactivePower2) - && minActivePower <= reducedActivePower1 && reducedActivePower1 <= maxActivePower - && minReactivePower <= reducedReactivePower1 && reducedReactivePower1 <= maxReactivePower) { - reducedActivePower = reducedActivePower1; - reducedReactivePower = reducedReactivePower1; - } else if (minActivePower <= reducedActivePower2 && reducedActivePower2 <= maxActivePower - && minReactivePower <= reducedReactivePower2 && reducedReactivePower2 <= maxReactivePower) { - reducedActivePower = reducedActivePower2; - reducedReactivePower = reducedReactivePower2; - } else { - log.error("Can't reduce power to fit the power limitations!"); - } - } else if (minActivePower > activePower || activePower > maxActivePower) { - // only activePower is out of allowed value range - if (minActivePower > activePower) { - // Discharge - reducedActivePower = minActivePower; - reducedReactivePower = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi); - } else { - // Charge - reducedActivePower = maxActivePower; - reducedReactivePower = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi); - } - } else { - // only reactivePower is out of allowed value range - if (minReactivePower > reactivePower) { - // Discharge - reducedReactivePower = minReactivePower; - reducedActivePower = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower, - cosPhi); - } else { - // Charge - reducedReactivePower = maxReactivePower; - reducedActivePower = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower, - cosPhi); - } - } - if (activePower != reducedActivePower || reactivePower != reducedReactivePower) { - log.info("Reduce activePower from [{}] to [{}] and reactivePower from [{}] to [{}]", - new Object[] { activePower, reducedActivePower, reactivePower, reducedReactivePower }); - } - this.activePower = reducedActivePower; - this.reactivePower = reducedReactivePower; - } - - /** - * Writes active and reactive power to the setActive-/setReactivePower Channel if the value was set - */ - public void writePower() { - this.reducePower(); - try { - // activePowerQueue.add(activePower); - if (activePowerValid) { - setActivePower.pushWrite(activePower); - } - // reactivePowerQueue.add(reactivePower); - if (reactivePowerValid) { - setReactivePower.pushWrite(reactivePower); - } - } catch (WriteChannelException e) { - log.error("Failed to reduce and set Power!"); - } - activePowerValid = false; - reactivePowerValid = false; - activePower = 0L; - reactivePower = 0L; - } - - /** - * Get the max active power out of allowedDischarge, allowedApparent and writeMax of setActivePower channels - * - * @return max allowed activePower - */ - public long getMaxActivePower() { - long maxPower = 0; - boolean valid = false; - if (allowedDischarge.valueOptional().isPresent()) { - maxPower = allowedDischarge.valueOptional().get(); - valid = true; - } - if (valid && allowedApparent.valueOptional().isPresent()) { - maxPower = Math.min(maxPower, allowedApparent.valueOptional().get()); - } else if (allowedApparent.valueOptional().isPresent()) { - maxPower = allowedApparent.valueOptional().get(); - valid = true; - } - if (valid && setActivePower.writeMax().isPresent()) { - maxPower = Math.min(maxPower, setActivePower.writeMax().get()); - } else if (setActivePower.writeMax().isPresent()) { - maxPower = setActivePower.writeMax().get(); - } - if (!valid) { - log.error("Failed to get Max value for ActivePower! Return 0."); - } - return maxPower; - } - - /** - * Get the min active power out of allowedCharge, allowedApparent and writeMin of setActivePower channels - * - * @return min allowed activePower - */ - public long getMinActivePower() { - long minPower = 0; - boolean valid = false; - if (allowedCharge.valueOptional().isPresent()) { - minPower = allowedCharge.valueOptional().get(); - valid = true; - } - if (valid && allowedApparent.valueOptional().isPresent()) { - minPower = Math.max(minPower, allowedApparent.valueOptional().get() * -1); - } else if (allowedApparent.valueOptional().isPresent()) { - minPower = allowedApparent.valueOptional().get() * -1; - valid = true; - } - if (valid && setActivePower.writeMin().isPresent()) { - minPower = Math.max(minPower, setActivePower.writeMin().get()); - } else if (setActivePower.writeMin().isPresent()) { - minPower = setActivePower.writeMin().get(); - } - if (!valid) { - log.error("Failed to get Min value for ActivePower! Return 0."); - } - return minPower; - } - - /** - * Get the max reactive power out of allowedDischarge, allowedApparent and writeMax of setReactivePower channels - * - * @return max allowed reactivePower - */ - public long getMaxReactivePower() { - long maxPower = 0; - boolean valid = false; - if (allowedApparent.valueOptional().isPresent()) { - maxPower = allowedApparent.valueOptional().get(); - valid = true; - } - if (valid && setReactivePower.writeMax().isPresent()) { - maxPower = Math.min(maxPower, setReactivePower.writeMax().get()); - } else if (setReactivePower.writeMax().isPresent()) { - maxPower = setReactivePower.writeMax().get(); - } - if (!valid) { - log.debug("Failed to get Max value for ReactivePower! Return 0."); - } - return maxPower; - } - - /** - * Get the min reactive power out of allowedCharge, allowedApparent and writeMin of setReactivePower channels - * - * @return min allowed reactivePower - */ - public long getMinReactivePower() { - long minPower = 0; - boolean valid = false; - if (allowedApparent.valueOptional().isPresent()) { - minPower = allowedApparent.valueOptional().get() * -1; - valid = true; - } - if (valid && setReactivePower.writeMin().isPresent()) { - minPower = Math.max(minPower, setReactivePower.writeMin().get()); - } else if (setReactivePower.writeMin().isPresent()) { - minPower = setReactivePower.writeMin().get(); - } - if (!valid) { - log.debug("Failed to get Min value for ReactivePower! Return 0."); - } - return minPower; - } - -} diff --git a/edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java b/edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java new file mode 100644 index 00000000000..d5986320878 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java @@ -0,0 +1,87 @@ +package io.openems.core.utilities.power; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.operation.distance.DistanceOp; +import com.vividsolutions.jts.operation.distance.GeometryLocation; + +public class CosPhiLineCharacteristicLimitation extends Limitation { + + private final TreeMap characteristic = new TreeMap<>(); + private Long xNull; + private Long yNull; + private Geometry line; + + public CosPhiLineCharacteristicLimitation(SymmetricPower power) { + super(power); + } + + public void setCosPhi(Long xNull, Long yNull, TreeMap characteristic) { + if (this.characteristic.equals(characteristic) || this.xNull != xNull || this.yNull != yNull) { + if (characteristic != null && !characteristic.isEmpty() && xNull != null && yNull != null) { + long maxApparentPower = power.getMaxApparentPower(); + boolean isFirs = true; + List coordinates = new ArrayList<>(); + Double y = null; + for (Entry point : characteristic.entrySet()) { + double cosPhi = point.getValue(); + double m = Math.tan(Math.acos(Math.abs(cosPhi))); + double x = point.getKey(); + double t = yNull - m * xNull; + if ((x < 0 && cosPhi > 0) || (x > 0 && cosPhi > 0)) { + m *= -1; + } + y = m * x + t; + if (isFirs) { + coordinates.add(new Coordinate(maxApparentPower * -1, y)); + isFirs = false; + } + coordinates.add(new Coordinate(x, y)); + } + if (y != null) { + coordinates.add(new Coordinate(maxApparentPower, y)); + } + line = SymmetricPowerImpl.getFactory() + .createLineString(coordinates.toArray(new Coordinate[coordinates.size()])); + } else { + line = null; + } + this.characteristic.clear(); + this.characteristic.putAll(characteristic); + this.xNull = xNull; + this.yNull = yNull; + notifyListeners(); + } + } + + @Override + protected Geometry applyLimit(Geometry geometry) throws PowerException { + if (line != null) { + Geometry newGeometry = geometry.intersection(line); + if (newGeometry.isEmpty()) { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + return SymmetricPowerImpl.getFactory().createPoint(location.getCoordinate()); + } + } + } else { + return newGeometry; + } + } + return geometry; + } + + @Override + public String toString() { + // TODO + return null; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java b/edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java new file mode 100644 index 00000000000..a3fd7a92b96 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java @@ -0,0 +1,71 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.operation.distance.DistanceOp; +import com.vividsolutions.jts.operation.distance.GeometryLocation; + +public class CosPhiLineLimitation extends Limitation { + + private Double cosPhi; + private Boolean capacitive; + private Long xNull; + private Long yNull; + private Geometry line; + + public CosPhiLineLimitation(SymmetricPower power) { + super(power); + } + + public void setCosPhi(Double cosPhi, Boolean capacitive, Long xNull, Long yNull) { + if (this.cosPhi != cosPhi || this.capacitive != capacitive || this.xNull != xNull || this.yNull != yNull) { + if (cosPhi != null && capacitive != null && xNull != null && yNull != null) { + long maxApparentPower = power.getMaxApparentPower(); + double m1 = Math.tan(Math.acos(Math.abs(cosPhi))); + if(capacitive) { + m1*=-1; + } + double m2 = m1 *-1; + double t1 = yNull - m1 * xNull; + double t2 = yNull - m2 * xNull; + double y1 = m1 * maxApparentPower + t1; + double y2 = m2 * maxApparentPower*-1 + t2; + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, y1), + new Coordinate(xNull, yNull), new Coordinate(maxApparentPower*-1, y2) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + }else { + line = null; + } + this.cosPhi = cosPhi; + this.capacitive = capacitive; + this.xNull = xNull; + this.yNull = yNull; + notifyListeners(); + } + } + + @Override + protected Geometry applyLimit(Geometry geometry) throws PowerException { + if(line != null) { + Geometry newGeometry = geometry.intersection(line); + if (newGeometry.isEmpty()) { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + return SymmetricPowerImpl.getFactory().createPoint(location.getCoordinate()); + } + } + } else { + return newGeometry; + } + } + return geometry; + } + + @Override + public String toString() { + return "Active- and reactivepower has to be on cosPhi "+cosPhi+" "+(capacitive?"inductive":"capacitive")+" with Zero at X: "+xNull+" Y: "+yNull; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/Limitation.java b/edge/src/io/openems/core/utilities/power/Limitation.java new file mode 100644 index 00000000000..fd922867f83 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/Limitation.java @@ -0,0 +1,39 @@ +package io.openems.core.utilities.power; + +import java.util.ArrayList; +import java.util.List; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public abstract class Limitation { + + protected static final Coordinate ZERO = new Coordinate(0, 0); + + protected SymmetricPower power; + private List listeners; + + public Limitation(SymmetricPower power) { + this.power = power; + this.listeners = new ArrayList<>(); + } + + protected void notifyListeners() { + for(LimitationChangedListener listener: listeners) { + listener.onLimitationChange(this); + } + } + + public void addListener(LimitationChangedListener listener) { + this.listeners.add(listener); + } + + public void removeListener(LimitationChangedListener listener) { + this.listeners.remove(listener); + } + + protected abstract Geometry applyLimit(Geometry geometry) throws PowerException; + + @Override + public abstract String toString(); +} diff --git a/edge/src/io/openems/core/utilities/power/LimitationChangedListener.java b/edge/src/io/openems/core/utilities/power/LimitationChangedListener.java new file mode 100644 index 00000000000..97d190dc7a2 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/LimitationChangedListener.java @@ -0,0 +1,6 @@ +package io.openems.core.utilities.power; + +public interface LimitationChangedListener { + + void onLimitationChange(Limitation sender); +} diff --git a/edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java b/edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java new file mode 100644 index 00000000000..58901c48271 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java @@ -0,0 +1,54 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +public class MaxCosPhiLimitation extends Limitation { + + private Geometry polygon; + private Double cosPhi; + + public MaxCosPhiLimitation(SymmetricPower power) { + super(power); + } + + public void setMaxCosPhi(Double cosPhi) { + if (cosPhi != this.cosPhi) { + if (cosPhi != null) { + Long maxApparentPower = this.power.getMaxApparentPower(); + GeometryFactory factory = SymmetricPowerImpl.getFactory(); + double m = Math.tan(Math.acos(cosPhi)); + double y = m * maxApparentPower; + Coordinate[] coordinates = new Coordinate[] { new Coordinate(ZERO), new Coordinate(maxApparentPower, y), + new Coordinate(maxApparentPower, y * -1), new Coordinate(ZERO), + new Coordinate(maxApparentPower * -1, y * -1), new Coordinate(maxApparentPower * -1, y), + new Coordinate(ZERO) }; + polygon = factory.createPolygon(coordinates); + } else { + polygon = null; + } + this.cosPhi = cosPhi; + notifyListeners(); + } + } + + @Override + public Geometry applyLimit(Geometry geometry) throws PowerException { + if (this.polygon != null) { + Geometry newGeometry = geometry.intersection(polygon); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The CosPhi limitation is too small! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "The Power has to be between CosPhi "+cosPhi+" inductive and capacitive."; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java b/edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java new file mode 100644 index 00000000000..b851b643643 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java @@ -0,0 +1,51 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public class NoPBetweenLimitation extends Limitation { + + private Geometry rect; + private Long pMin; + private Long pMax; + + public NoPBetweenLimitation(SymmetricPower power) { + super(power); + } + + public void setP(Long pMin, Long pMax) { + if (pMin != this.pMin || pMax != this.pMax) { + if (pMin != null && pMax != null) { + long qMin = power.getMaxApparentPower() * -1; + long qMax = power.getMaxApparentPower(); + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin+0.1, qMax), new Coordinate(pMin+0.1, qMin), + new Coordinate(pMax-0.1, qMin), new Coordinate(pMax-0.1, qMax), new Coordinate(pMin+0.1, qMax) }; + rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); + } else { + rect = null; + } + this.pMin = pMin; + this.pMax = pMax; + notifyListeners(); + } + } + + @Override + protected Geometry applyLimit(Geometry geometry) throws PowerException { + if (rect != null) { + Geometry newGeometry = geometry.difference(rect); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ActivePower limitation is too large! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No activepower between "+pMin+" and "+pMax+"."; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java b/edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java new file mode 100644 index 00000000000..e53fc0b7a24 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java @@ -0,0 +1,51 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public class NoQBetweenLimitation extends Limitation { + + private Geometry rect; + private Long qMin; + private Long qMax; + + public NoQBetweenLimitation(SymmetricPower power) { + super(power); + } + + public void setQ(Long qMin, Long qMax) { + if (qMin != this.qMin || qMax != this.qMax) { + if (qMin != null && qMax != null) { + long pMin = power.getMaxApparentPower() * -1; + long pMax = power.getMaxApparentPower(); + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax-0.1), new Coordinate(pMin, qMin+0.1), + new Coordinate(pMax, qMin+0.1), new Coordinate(pMax, qMax-0.1), new Coordinate(pMin, qMax-0.1) }; + rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); + } else { + rect = null; + } + this.qMin = qMin; + this.qMax = qMax; + notifyListeners(); + } + } + + @Override + protected Geometry applyLimit(Geometry geometry) throws PowerException { + if (rect != null) { + Geometry newGeometry = geometry.difference(rect); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ReactivePower limitation is too large! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No reactivepower between "+qMin+" and "+qMax+"."; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/PEqualLimitation.java b/edge/src/io/openems/core/utilities/power/PEqualLimitation.java new file mode 100644 index 00000000000..cd102a3b6af --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/PEqualLimitation.java @@ -0,0 +1,78 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.operation.distance.DistanceOp; +import com.vividsolutions.jts.operation.distance.GeometryLocation; + +public class PEqualLimitation extends Limitation { + + private Geometry line; + private Long p; + + public PEqualLimitation(SymmetricPower power) { + super(power); + } + + public void setP(Long p) { + if (p != this.p) { + if (p != null) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(p, power.getMaxApparentPower()), + new Coordinate(p, power.getMaxApparentPower() * -1) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + } else { + line = null; + } + this.p = p; + notifyListeners(); + } + } + + @Override + protected Geometry applyLimit(Geometry geometry) throws PowerException { + if (line != null) { + Geometry newGeometry = geometry.intersection(line); + long maxApparentPower = power.getMaxApparentPower(); + if (newGeometry.isEmpty()) { + Geometry smallerP = SymmetricPower.intersectRect(geometry, 0, p, maxApparentPower * -1, + maxApparentPower); + if (!smallerP.isEmpty()) { + DistanceOp distance = new DistanceOp(smallerP, line); + GeometryLocation[] locations = distance.nearestLocations(); + long maxP = 0; + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + maxP = (long) location.getCoordinate().x; + break; + } + } + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxP, maxApparentPower), + new Coordinate(maxP, maxApparentPower * -1) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + return geometry.intersection(line); + } else { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + Coordinate[] coordinates = new Coordinate[] { + new Coordinate(location.getCoordinate().x, maxApparentPower), + new Coordinate(location.getCoordinate().x, maxApparentPower * -1) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + return geometry.intersection(line); + } + } + } + } else { + return newGeometry; + } + } + return geometry; + } + + @Override + public String toString() { + return "ActivePower has to be "+p+" W"; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java b/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java new file mode 100644 index 00000000000..ac2b94f8628 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java @@ -0,0 +1,51 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public class PGreaterEqualLimitation extends Limitation { + + private Geometry rect; + private Long p; + + public PGreaterEqualLimitation(SymmetricPower power) { + super(power); + } + + public void setP(Long p) { + if (p != this.p) { + if (p != null) { + long pMin = p; + long pMax = power.getMaxApparentPower(); + long qMin = power.getMaxApparentPower() * -1; + long qMax = power.getMaxApparentPower(); + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), + new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; + rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); + } else { + rect = null; + } + this.p = p; + notifyListeners(); + } + } + + @Override + public Geometry applyLimit(Geometry geometry) throws PowerException { + if (rect != null) { + Geometry newGeometry = geometry.intersection(this.rect); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ActivePower limitation is too big! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No activepower below "+p+"."; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java b/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java new file mode 100644 index 00000000000..f735c7e21d3 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java @@ -0,0 +1,51 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public class PSmallerEqualLimitation extends Limitation { + + private Geometry rect; + private Long p; + + public PSmallerEqualLimitation(SymmetricPower power) { + super(power); + } + + public void setP(Long p) { + if (p != this.p) { + if (p != null) { + long pMin = power.getMaxApparentPower() * -1; + long pMax = p; + long qMin = power.getMaxApparentPower() * -1; + long qMax = power.getMaxApparentPower(); + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), + new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; + rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); + } else { + rect = null; + } + this.p = p; + notifyListeners(); + } + } + + @Override + public Geometry applyLimit(Geometry geometry) throws PowerException { + if (rect != null) { + Geometry newGeometry = geometry.intersection(this.rect); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ActivePower limitation is too small! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No activepower above "+p+"."; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/PowerChangeListener.java b/edge/src/io/openems/core/utilities/power/PowerChangeListener.java new file mode 100644 index 00000000000..1b516652246 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/PowerChangeListener.java @@ -0,0 +1,7 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Geometry; + +public interface PowerChangeListener { + void powerChanged(Geometry allowedPower); +} diff --git a/edge/src/io/openems/core/utilities/power/PowerResetListener.java b/edge/src/io/openems/core/utilities/power/PowerResetListener.java new file mode 100644 index 00000000000..7c91b202942 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/PowerResetListener.java @@ -0,0 +1,9 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Geometry; + +public interface PowerResetListener { + + Geometry afterPowerReset(Geometry allowedPower); + +} diff --git a/edge/src/io/openems/core/utilities/power/QEqualLimitation.java b/edge/src/io/openems/core/utilities/power/QEqualLimitation.java new file mode 100644 index 00000000000..954dea67cbf --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/QEqualLimitation.java @@ -0,0 +1,76 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.operation.distance.DistanceOp; +import com.vividsolutions.jts.operation.distance.GeometryLocation; + +public class QEqualLimitation extends Limitation { + + private Geometry line; + private Long q; + + public QEqualLimitation(SymmetricPower power) { + super(power); + } + + public void setQ(Long q) { + if (q != this.q) { + if (q != null) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(power.getMaxApparentPower(), q), + new Coordinate(power.getMaxApparentPower() * -1, q) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + } else { + line = null; + } + this.q = q; + notifyListeners(); + } + } + + @Override + protected Geometry applyLimit(Geometry geometry) throws PowerException { + if (line != null) { + Geometry newGeometry = geometry.intersection(line); + long maxApparentPower = power.getMaxApparentPower(); + if (newGeometry.isEmpty()) { + Geometry smallerQ = SymmetricPowerImpl.intersectRect(geometry, maxApparentPower * -1, maxApparentPower, 0, q); + if (!smallerQ.isEmpty()) { + DistanceOp distance = new DistanceOp(smallerQ, line); + GeometryLocation[] locations = distance.nearestLocations(); + long maxQ = 0; + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + maxQ = (long) location.getCoordinate().y; + break; + } + } + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, maxQ), + new Coordinate(maxApparentPower * -1, maxQ) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + return geometry.intersection(line); + } else { + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, location.getCoordinate().y), + new Coordinate(maxApparentPower * -1, location.getCoordinate().y) }; + line = SymmetricPowerImpl.getFactory().createLineString(coordinates); + return geometry.intersection(line); + } + } + } + } else { + return newGeometry; + } + } + return geometry; + } + + @Override + public String toString() { + return "ReactivePower has to be "+q+" Var"; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java b/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java new file mode 100644 index 00000000000..84af26b58fa --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java @@ -0,0 +1,51 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public class QGreaterEqualLimitation extends Limitation { + + private Geometry rect; + private Long q; + + public QGreaterEqualLimitation(SymmetricPower power) { + super(power); + } + + public void setQ(Long q) { + if (q != this.q) { + if (q != null) { + long pMin = power.getMaxApparentPower() * -1; + long pMax = power.getMaxApparentPower(); + long qMin = q; + long qMax = power.getMaxApparentPower(); + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), + new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; + rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); + } else { + rect = null; + } + this.q = q; + notifyListeners(); + } + } + + @Override + public Geometry applyLimit(Geometry geometry) throws PowerException { + if (rect != null) { + Geometry newGeometry = geometry.intersection(this.rect); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ReactivePower limitation is too big! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No reactivepower below "+q+"."; + } + +} diff --git a/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java b/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java new file mode 100644 index 00000000000..fb868b3a9d9 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java @@ -0,0 +1,50 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; + +public class QSmallerEqualLimitation extends Limitation { + + private Geometry rect; + private Long q; + + public QSmallerEqualLimitation(SymmetricPower power) { + super(power); + } + + public void setQ(Long q) { + if (q != this.q) { + if (q != null) { + long pMin = power.getMaxApparentPower() * -1; + long pMax = power.getMaxApparentPower(); + long qMin = power.getMaxApparentPower() * -1; + long qMax = q; + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), + new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; + rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); + } else { + rect = null; + } + this.q = q; + notifyListeners(); + } + } + + @Override + public Geometry applyLimit(Geometry geometry) throws PowerException { + if (rect != null) { + Geometry newGeometry = geometry.intersection(this.rect); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ReactivePower limitation is too small! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No reactivepower above "+q+"."; + } +} diff --git a/edge/src/io/openems/core/utilities/power/SMaxLimitation.java b/edge/src/io/openems/core/utilities/power/SMaxLimitation.java new file mode 100644 index 00000000000..e0c1a642d8d --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/SMaxLimitation.java @@ -0,0 +1,49 @@ +package io.openems.core.utilities.power; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.util.GeometricShapeFactory; + +public class SMaxLimitation extends Limitation { + + private Geometry circle; + private Long sMax; + + public SMaxLimitation(SymmetricPower power) { + super(power); + } + + public void setSMax(Long sMax, Long xNull, Long yNull) { + if (sMax != this.sMax) { + if (sMax != null) { + GeometricShapeFactory shapeFactory = SymmetricPowerImpl.getShapefactory(); + shapeFactory.setCentre(new Coordinate(xNull,yNull)); + shapeFactory.setSize(sMax*2); + shapeFactory.setNumPoints(32); + this.circle = shapeFactory.createCircle(); + } else { + this.circle = null; + } + this.sMax = sMax; + notifyListeners(); + } + } + + @Override + public Geometry applyLimit(Geometry geometry) throws PowerException { + if (this.circle != null) { + Geometry newGeometry = geometry.intersection(this.circle); + if (newGeometry.isEmpty()) { + throw new PowerException( + "The ApparentPower limitation is too small! There needs to be at least one point after the limitation."); + } + return newGeometry; + } + return geometry; + } + + @Override + public String toString() { + return "No apparentpower greater than "+sMax+"."; + } +} diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPower.java b/edge/src/io/openems/core/utilities/power/SymmetricPower.java index 528a8748070..493a48dffae 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPower.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPower.java @@ -1,16 +1,15 @@ package io.openems.core.utilities.power; import java.awt.Color; -import java.awt.Graphics2D; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import javax.swing.JFrame; -import javax.swing.JPanel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.vividsolutions.jts.awt.ShapeWriter; import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; @@ -19,375 +18,229 @@ import com.vividsolutions.jts.operation.distance.GeometryLocation; import com.vividsolutions.jts.util.GeometricShapeFactory; -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.core.utilities.ControllerUtils; +public abstract class SymmetricPower { + /* + * static + */ + protected static final GeometryFactory FACTORY = new GeometryFactory(); + protected static final GeometricShapeFactory SHAPEFACTORY = new GeometricShapeFactory(FACTORY); + private static final SVGWriter writer = new SVGWriter(); + private static final Color[] COLORS = new Color[] { Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW, + Color.ORANGE, Color.RED }; + protected static final Coordinate ZERO = new Coordinate(0,0); + + public static GeometryFactory getFactory() { + return FACTORY; + } -public class SymmetricPower { + public static GeometricShapeFactory getShapefactory() { + return SHAPEFACTORY; + } + + public static Geometry intersectRect(Geometry base, double pMin, double pMax, double qMin, double qMax) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), + new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; + Geometry rect = FACTORY.createPolygon(coordinates); + return base.intersection(rect); + } - private final long maxApparentPower; - private final GeometryFactory factory; - private final Point zero; + /* + * Fields + */ + protected final Logger log = LoggerFactory.getLogger(this.getClass()); private Geometry geometry; - private final ShapeWriter sw; private final List geometries; - private final SVGWriter writer; - private static final Color[] colors = new Color[] { Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW, - Color.ORANGE, Color.RED }; - private ReadChannel currentActivePower; - private ReadChannel currentReactivePower; - private ReadChannel allowedApparent; - private ReadChannel allowedCharge; - private ReadChannel allowedDischarge; - private WriteChannel setActivePower; - private WriteChannel setReactivePower; - - public SymmetricPower(long maxApparentPower, ReadChannel currentActivePower, - ReadChannel currentReactivePower, ReadChannel allowedApparent, ReadChannel allowedCharge, - ReadChannel allowedDischarge, WriteChannel setActivePower, - WriteChannel setReactivePower) throws PowerException { - this.maxApparentPower = Math.abs(maxApparentPower); - this.currentActivePower = currentActivePower; - this.currentReactivePower = currentReactivePower; - this.allowedApparent = allowedApparent; - this.allowedCharge = allowedCharge; - this.allowedDischarge = allowedDischarge; - this.setActivePower = setActivePower; - this.setReactivePower = setReactivePower; - this.factory = new GeometryFactory(); - this.zero = factory.createPoint(new Coordinate(0, 0)); - this.sw = new ShapeWriter(); - this.writer = new SVGWriter(); + private Optional minP; + private Optional maxP; + private Optional minQ; + private Optional maxQ; + private long maxApparentPower = 0; + private final List resetListeners; + private final List changeListeners; + + public SymmetricPower() { this.geometries = new ArrayList<>(); - reset(); + this.resetListeners = new ArrayList<>(); + this.changeListeners = new ArrayList<>(); } - public Optional getCurrentP() { - return this.currentActivePower.valueOptional(); + /* + * Methods + */ + + public long getMaxApparentPower() { + return maxApparentPower; } - public Optional getCurrentQ() { - return this.currentReactivePower.valueOptional(); + protected void setMaxApparentPower(long power) { + this.maxApparentPower = Math.abs(power); } - public Optional getCurrentS() { - if (getCurrentP().isPresent() && getCurrentQ().isPresent()) { - return Optional.of(ControllerUtils.calculateApparentPower(getCurrentP().get(), getCurrentQ().get())); - } else { - return Optional.empty(); - } + public Geometry getGeometry() { + // synchronized (this.geometry) { + return this.geometry; + // } } - private void setGeometry(Geometry g) { + protected void setGeometry(Geometry g) { + // synchronized (this.geometry) { this.geometry = g; this.geometries.add(g); + this.calculateMinMax(); + for (PowerChangeListener listener : this.changeListeners) { + listener.powerChanged(g); + } + // } } - public void reset() throws PowerException { - this.geometries.clear(); - GeometricShapeFactory shape = new GeometricShapeFactory(factory); - shape.setCentre(new Coordinate(0, 0)); - shape.setSize(this.maxApparentPower * 2); - shape.setNumPoints(32); - setGeometry(shape.createCircle()); - setSMax(this.allowedApparent.valueOptional().orElse(0L)); - setPGreaterOrEqual(this.allowedCharge.valueOptional().orElse(0L)); - setPSmallerOrEqual(this.allowedDischarge.valueOptional().orElse(0L)); + private void calculateMinMax() { + this.maxP = Optional.ofNullable(getClosestP(maxApparentPower)); + this.minP = Optional.ofNullable(getClosestP(maxApparentPower * -1)); + this.maxQ = Optional.ofNullable(getClosestQ(maxApparentPower)); + this.minQ = Optional.ofNullable(getClosestQ(maxApparentPower * -1)); } - public void setSMax(long smax) throws PowerException { - GeometricShapeFactory shape = new GeometricShapeFactory(factory); - shape.setCentre(new Coordinate(0, 0)); - shape.setSize(this.maxApparentPower); - shape.setNumPoints(32); - Geometry newGeometry = geometry.intersection(shape.createCircle()); - if (newGeometry.isEmpty()) { - // TODO throw exception - throw new PowerException(""); + private Long getClosestP(long p) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(p, maxApparentPower), + new Coordinate(p, maxApparentPower * -1) }; + LineString line = FACTORY.createLineString(coordinates); + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + return (long) location.getCoordinate().x; + } } - setGeometry(newGeometry); + return null; } - public void setPGreaterOrEqual(long p) throws PowerException { - Geometry newGeometry = intersectRect(this.geometry, p - 0.1, maxApparentPower, maxApparentPower * -1, - maxApparentPower); - if (newGeometry.isEmpty()) { - throw new PowerException(""); + private Long getClosestQ(long q) { + Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, q), + new Coordinate(maxApparentPower * -1, q) }; + LineString line = FACTORY.createLineString(coordinates); + DistanceOp distance = new DistanceOp(geometry, line); + GeometryLocation[] locations = distance.nearestLocations(); + for (GeometryLocation location : locations) { + if (!location.getGeometryComponent().equals(line)) { + return (long) location.getCoordinate().y; + } } - setGeometry(newGeometry); + return null; } - public void setPSmallerOrEqual(long p) throws PowerException { - Geometry newGeometry = intersectRect(this.geometry, maxApparentPower * -1, p + 0.1, maxApparentPower * -1, - maxApparentPower); - if (newGeometry.isEmpty()) { - throw new PowerException(""); - } - setGeometry(newGeometry); + public void addListener(PowerResetListener listener) { + this.resetListeners.add(listener); } - public void setQGreaterOrEqual(long q) throws PowerException { - Geometry newGeometry = intersectRect(this.geometry, maxApparentPower * -1, maxApparentPower, q - 0.1, - maxApparentPower); - if (newGeometry.isEmpty()) { - throw new PowerException(""); - } - setGeometry(newGeometry); + public void removeListener(PowerResetListener listener) { + this.resetListeners.add(listener); } - public void setQSmallerOrEqual(long q) throws PowerException { - Geometry newGeometry = intersectRect(this.geometry, maxApparentPower * -1, maxApparentPower, - maxApparentPower * -1, q + 0.1); - if (newGeometry.isEmpty()) { - throw new PowerException(""); - } - setGeometry(newGeometry); + public void addListener(PowerChangeListener listener) { + this.changeListeners.add(listener); } - public void setNoQBetween(long qmin, long qmax) throws PowerException { - Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, qmin), - new Coordinate(maxApparentPower * -1, qmin), new Coordinate(maxApparentPower * -1, qmax), - new Coordinate(maxApparentPower, qmax), new Coordinate(maxApparentPower, qmin) }; - Geometry rect = this.factory.createPolygon(coordinates); - Geometry newGeometry = this.geometry.difference(rect); - if (newGeometry.isEmpty()) { - // TODO throw exception - throw new PowerException(""); - } - setGeometry(newGeometry); + public void removeListener(PowerChangeListener listener) { + this.changeListeners.add(listener); } - /** - * - * @param pmin - * is exclusive - * @param pmax - * is exclusive - * @throws PowerException - */ - public void setNoPBetween(long pmin, long pmax) throws PowerException { - Coordinate[] coordinates = new Coordinate[] { new Coordinate(pmin, maxApparentPower), - new Coordinate(pmin, maxApparentPower * -1), new Coordinate(pmax, maxApparentPower * -1), - new Coordinate(pmax, maxApparentPower), new Coordinate(pmin, maxApparentPower) }; - Geometry rect = this.factory.createPolygon(coordinates); - Geometry newGeometry = this.geometry.difference(rect); - if (newGeometry.isEmpty()) { - // TODO throw exception - throw new PowerException(""); - } - setGeometry(newGeometry); - } + public abstract void applyLimitation(Limitation limit) throws PowerException; - public void setMaxCosPhi(double cosPhi) throws PowerException { - double m = Math.tan(Math.acos(cosPhi)); - double y = m * maxApparentPower; - Coordinate[] coordinates = new Coordinate[] { new Coordinate(0, 0), new Coordinate(maxApparentPower, y), - new Coordinate(maxApparentPower, y * -1), new Coordinate(0, 0), - new Coordinate(maxApparentPower * -1, y * -1), new Coordinate(maxApparentPower * -1, y), - new Coordinate(0, 0) }; - Geometry polygon = this.factory.createPolygon(coordinates); - Geometry newGeometry = this.geometry.intersection(polygon); - if (newGeometry.isEmpty()) { - // TODO throw exception - throw new PowerException(""); - } - setGeometry(newGeometry); + public Optional getMaxP() { + return this.maxP; } - private Geometry intersectRect(Geometry base, double pMin, double pMax, double qMin, double qMax) { - Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), - new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; - Geometry rect = this.factory.createPolygon(coordinates); - return this.geometry.intersection(rect); + public Optional getMinP() { + return this.minP; } - public void setP(long p) { - Coordinate[] coordinates = new Coordinate[] { new Coordinate(p, maxApparentPower), - new Coordinate(p, maxApparentPower * -1) }; - LineString line = this.factory.createLineString(coordinates); - Geometry newGeometry = this.geometry.intersection(line); - if (newGeometry.isEmpty()) { - Geometry smallerP = intersectRect(this.geometry, 0, p, maxApparentPower * -1, maxApparentPower); - if (!smallerP.isEmpty()) { - DistanceOp distance = new DistanceOp(smallerP, line); - GeometryLocation[] locations = distance.nearestLocations(); - long maxP = 0; - for (GeometryLocation location : locations) { - if (!location.getGeometryComponent().equals(line)) { - maxP = (long) location.getCoordinate().x; - break; - } - } - coordinates = new Coordinate[] { new Coordinate(maxP, maxApparentPower), - new Coordinate(maxP, maxApparentPower * -1) }; - line = this.factory.createLineString(coordinates); - setGeometry(this.geometry.intersection(line)); - } else { - DistanceOp distance = new DistanceOp(geometry, line); - GeometryLocation[] locations = distance.nearestLocations(); - for (GeometryLocation location : locations) { - if (!location.getGeometryComponent().equals(line)) { - coordinates = new Coordinate[] { new Coordinate(location.getCoordinate().x, maxApparentPower), - new Coordinate(location.getCoordinate().x, maxApparentPower * -1) }; - line = this.factory.createLineString(coordinates); - setGeometry(this.geometry.intersection(line)); - break; - } - } - } - } else { - setGeometry(newGeometry); - } + public Optional getMaxQ() { + return this.maxQ; } - public void setQ(long q) { - Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, q), - new Coordinate(maxApparentPower * -1, q) }; - LineString line = this.factory.createLineString(coordinates); - Geometry newGeometry = this.geometry.intersection(line); - if (newGeometry.isEmpty()) { - Geometry smallerQ = intersectRect(this.geometry, maxApparentPower * -1, maxApparentPower, 0, q); - if (!smallerQ.isEmpty()) { - DistanceOp distance = new DistanceOp(smallerQ, line); - GeometryLocation[] locations = distance.nearestLocations(); - long maxQ = 0; - for (GeometryLocation location : locations) { - if (!location.getGeometryComponent().equals(line)) { - maxQ = (long) location.getCoordinate().y; - break; - } - } - coordinates = new Coordinate[] { new Coordinate(maxApparentPower, maxQ), - new Coordinate(maxApparentPower * -1, maxQ) }; - line = this.factory.createLineString(coordinates); - setGeometry(this.geometry.intersection(line)); - } else { - DistanceOp distance = new DistanceOp(geometry, line); - GeometryLocation[] locations = distance.nearestLocations(); - for (GeometryLocation location : locations) { - if (!location.getGeometryComponent().equals(line)) { - coordinates = new Coordinate[] { new Coordinate(maxApparentPower, location.getCoordinate().y), - new Coordinate(maxApparentPower * -1, location.getCoordinate().y) }; - line = this.factory.createLineString(coordinates); - setGeometry(this.geometry.intersection(line)); - break; - } - } - } - } else { - setGeometry(newGeometry); - } + public Optional getMinQ() { + return this.minQ; } - public void setPQ(double m, long t) { - double y1 = m * (maxApparentPower * -1) + t; - double y2 = m * maxApparentPower + t; - Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower * -1, y1), - new Coordinate(maxApparentPower, y2) }; - LineString line = this.factory.createLineString(coordinates); - Geometry newGeometry = this.geometry.intersection(line); - if (newGeometry.isEmpty()) { - DistanceOp distance = new DistanceOp(geometry, line); - GeometryLocation[] locations = distance.nearestLocations(); - for (GeometryLocation location : locations) { - if (!location.getGeometryComponent().equals(line)) { - setGeometry(this.factory.createPoint(location.getCoordinate())); - break; - } + protected void reset() { + this.geometries.clear(); + for (PowerResetListener listener : this.resetListeners) { + Geometry g = listener.afterPowerReset(getGeometry()); + if (!g.isEmpty()) { + setGeometry(g); } - } else { - setGeometry(newGeometry); } } - public void printPower() { - JFrame frame = new JFrame("asdf"); - frame.setVisible(true); - frame.setSize(600, 400); - JPanel panel = new JPanel() { - @Override - protected void paintComponent(java.awt.Graphics g) { - super.paintComponent(g); - Graphics2D g2 = (Graphics2D) g; - g2.translate((int) maxApparentPower, (int) maxApparentPower); - int i = 0; - for (Geometry geo : geometries) { - g2.draw(sw.toShape(geo)); - i++; - i %= colors.length; - g2.setColor(colors[i]); - } - g2.translate((int) maxApparentPower * -1, (int) maxApparentPower * -1); - }; - }; - panel.setLayout(null); - frame.add(panel); - frame.validate(); - frame.repaint(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - - public long getP() { - return (long) this.reduceToZero().getCoordinate().y; - } - - public long getQ() { - return (long) this.reduceToZero().getCoordinate().y; - } - - private Point reduceToZero() { - if (this.geometry instanceof Point) { - return (Point) this.geometry; + protected Point reduceToZero() { + if (getGeometry() instanceof Point) { + return (Point) getGeometry(); } - DistanceOp distance = new DistanceOp(geometry, this.zero); + Point pZero = FACTORY.createPoint(ZERO); + DistanceOp distance = new DistanceOp(getGeometry(), pZero); GeometryLocation[] locations = distance.nearestLocations(); - Point result = this.zero; + Point result = pZero; for (GeometryLocation location : locations) { Geometry g = location.getGeometryComponent(); - if (!g.equals(this.zero) && g instanceof Point) { + if (!g.equals(pZero) && g instanceof Point) { result = (Point) location.getGeometryComponent(); break; } } - setGeometry(result); return result; } public String getAsSVG() { StringBuffer text = new StringBuffer(); - String viewBox = this.maxApparentPower * -1 + " " + this.maxApparentPower * -1 + " " + this.maxApparentPower * 2 - + " " + this.maxApparentPower * 2; + String viewBox = getMaxApparentPower() * -1 + " " + getMaxApparentPower() * -1 + " " + getMaxApparentPower() * 2 + + " " + getMaxApparentPower() * 2; text.append("\n"); text.append( "\n"); text.append("\n"); - // String name = testable.getName() == null ? "" : testable.getName(); - // String description = testable.getDescription() == null ? "" : testable.getDescription(); - // text.append(" \"" + name + "\",\n"); - // text.append(" " + description + "\n"); int i = 0; for (Geometry geo : geometries) { - String a = writeGeometryStyled(geo, "#" + Integer.toHexString(colors[i].getRGB()).substring(2)); + String color = "#" + Integer.toHexString(COLORS[i].getRGB()).substring(2); + String a = writeGeometryStyled(geo, color, color); text.append(a + "\n"); text.append("\n"); i++; - i %= colors.length; + i %= COLORS.length; } text.append("\n"); return text.toString(); } - private String writeGeometryStyled(Geometry g, String fillClr) { - String s = "\n"; + public static String getAsSVG(Geometry geometrie) { + StringBuffer text = new StringBuffer(); + + Envelope env = geometrie.getEnvelopeInternal(); + String viewBox = env.getMinX() + " " + env.getMinY() + " " + env.getMaxX() * 2 + " " + env.getMaxY() * 2; + + text.append("\n"); + text.append( + "\n"); + text.append("\n"); + String color = "#" + Integer.toHexString(COLORS[0].getRGB()).substring(2); + String a = writeGeometryStyled(geometrie, color, color); + text.append(a + "\n"); + text.append("\n"); + text.append("\n"); + return text.toString(); + } + + protected static String writeGeometryStyled(Geometry g, String fillClr, String strokeClr) { + String s = "\n"; s += write(g); s += ""; return s; } - private String write(Geometry geometry) { + private static String write(Geometry geometry) { if (geometry == null) { return ""; } diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java new file mode 100644 index 00000000000..6e51e76db93 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java @@ -0,0 +1,263 @@ +package io.openems.core.utilities.power; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.vividsolutions.jts.densify.Densifier; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.geom.util.AffineTransformation; + +import io.openems.api.device.nature.ess.SymmetricEssNature; + +public class SymmetricPowerClusterImpl extends SymmetricPower implements PowerChangeListener { + + private List dynamicLimitations; + private List ess; + + public SymmetricPowerClusterImpl() { + this.dynamicLimitations = new ArrayList<>(); + this.ess = new ArrayList<>(); + } + + public void addEss(SymmetricEssNature ess) { + this.ess.add(ess); + mergePower(); + ess.getPower().addListener(this); + setMaxApparentPower(getMaxApparentPower() + ess.maxNominalPower().valueOptional().orElse(0L)); + } + + public void removeEss(SymmetricEssNature ess) { + this.ess.remove(ess); + mergePower(); + ess.getPower().addListener(this); + setMaxApparentPower(getMaxApparentPower() - ess.maxNominalPower().valueOptional().orElse(0L)); + } + + @Override + public void powerChanged(Geometry allowedPower) { + mergePower(); + } + + private void mergePower() { + Geometry base = null; + for (SymmetricEssNature ess : this.ess) { + if (base != null) { + base = getUnionAround(base, ess.getPower().getGeometry()); + } else { + base = ess.getPower().getGeometry(); + } + } + for (Limitation limit : this.dynamicLimitations) { + Geometry limitedPower; + try { + limitedPower = limit.applyLimit(base); + if (!limitedPower.isEmpty()) { + base = limitedPower; + } + } catch (PowerException e) { + log.error("Failed to apply Limit after base Power changed!", e); + } + } + setGeometry(base); + } + + private Geometry getUnionAround(Geometry g1, Geometry g2) { + Geometry g1dens = Densifier.densify(g1, 10000); + Geometry g2dens = Densifier.densify(g2, 10000); + List geometries = new ArrayList<>(); + geometries.add(g1); + for (Coordinate c : g1dens.getCoordinates()) { + geometries.add(AffineTransformation.translationInstance(c.x, c.y).transform(g2)); + } + geometries.add(g2); + for (Coordinate c : g2dens.getCoordinates()) { + geometries.add(AffineTransformation.translationInstance(c.x, c.y).transform(g1)); + } + GeometryCollection collection = new GeometryCollection(geometries.toArray(new Geometry[geometries.size()]), + FACTORY); + return collection.union(); + } + + @Override + public void applyLimitation(Limitation limit) throws PowerException { + Geometry limitedPower = limit.applyLimit(getGeometry()); + if (!limitedPower.isEmpty()) { + setGeometry(limitedPower); + this.dynamicLimitations.add(limit); + } else { + throw new PowerException("No possible Power after applying Limit. Limit is not applied!"); + } + } + + private void setPower() { + Point p = reduceToZero(); + long activePower = (long) p.getCoordinate().x; + long reactivePower = (long) p.getCoordinate().y; + long socSum = 0; + for (SymmetricEssNature ess : this.ess) { + socSum += ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0); + } + if (activePower > 0) { + /* + * Discharge + */ + // sort ess by useableSoc asc + Collections.sort(ess, (a, b) -> { + return (int) ((a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0)) + - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0))); + }); + for (int i = 0; i < ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minP = activePower; + for (int j = i + 1; j < this.ess.size(); j++) { + if (this.ess.get(j).soc().valueOptional().orElse(0L) + - this.ess.get(j).minSoc().valueOptional().orElse(0) > 0) { + minP -= this.ess.get(j).getPower().getMaxP().orElse(0L); + } + } + if (minP < 0) { + minP = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxP = ess.getPower().getMaxP().orElse(0L); + if (activePower < maxP) { + maxP = activePower; + } + double diff = maxP - minP; + /* + * weight the range of possible power by the useableSoc + * if the useableSoc is negative the ess will be charged + */ + long power = (long) (Math.ceil(minP + diff / socSum + * (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); + PEqualLimitation limit = new PEqualLimitation(ess.getPower()); + limit.setP(power); + try { + ess.getPower().applyLimitation(limit); + activePower -= power; + } catch (PowerException e) { + log.error("Failed to set activePower on " + ess.id()); + } + } + } else { + /* + * Charge + */ + /* + * sort ess by 100 - useabelSoc + * 100 - 90 = 10 + * 100 - 45 = 55 + * 100 - (- 5) = 105 + * => ess with negative useableSoc will be charged much more then one with positive useableSoc + */ + Collections.sort(this.ess, (a, b) -> { + return (int) ((100 - (a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0))) + - (100 - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0)))); + }); + for (int i = 0; i < this.ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minP = activePower; + for (int j = i + 1; j < this.ess.size(); j++) { + minP -= this.ess.get(j).getPower().getMinP().orElse(0L); + } + if (minP > 0) { + minP = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxP = ess.getPower().getMinP().orElse(0L); + if (activePower > maxP) { + maxP = activePower; + } + double diff = maxP - minP; + // weight the range of possible power by the useableSoc + long power = (long) Math.floor(minP + diff / (this.ess.size() * 100 - socSum) + * (100 - (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); + PEqualLimitation limit = new PEqualLimitation(ess.getPower()); + limit.setP(power); + try { + ess.getPower().applyLimitation(limit); + activePower -= power; + } catch (PowerException e) { + log.error("Failed to set activePower on " + ess.id()); + } + } + } + + // sort ess by maxNominalPower asc + Collections.sort(ess, (a, b) -> { + return (int) (a.maxNominalPower().valueOptional().orElse(0L) + - b.maxNominalPower().valueOptional().orElse(0L)); + }); + if (reactivePower > 0) { + for (int i = 0; i < ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minQ = reactivePower; + for (int j = i + 1; j < this.ess.size(); j++) { + if (this.ess.get(j).maxNominalPower().valueOptional().orElse(0L) > 0) { + minQ -= this.ess.get(j).getPower().getMaxQ().orElse(0L); + } + } + if (minQ < 0) { + minQ = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxQ = ess.getPower().getMaxQ().orElse(0L); + if (reactivePower < maxQ) { + maxQ = reactivePower; + } + double diff = maxQ - minQ; + /* + * weight the range of possible power by the useableSoc + * if the useableSoc is negative the ess will be charged + */ + long power = (long) (Math.ceil(minQ + diff / getMaxApparentPower() + * ess.maxNominalPower().valueOptional().orElse(0L))); + QEqualLimitation limit = new QEqualLimitation(ess.getPower()); + limit.setQ(power); + try { + ess.getPower().applyLimitation(limit); + reactivePower -= power; + } catch (PowerException e) { + log.error("Failed to set reactivePower on " + ess.id()); + } + } + } else { + for (int i = 0; i < this.ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minQ = reactivePower; + for (int j = i + 1; j < this.ess.size(); j++) { + minQ -= this.ess.get(j).getPower().getMinQ().orElse(0L); + } + if (minQ > 0) { + minQ = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxQ = ess.getPower().getMinQ().orElse(0L); + if (reactivePower > maxQ) { + maxQ = reactivePower; + } + double diff = maxQ - minQ; + // weight the range of possible power by the useableSoc + long power = (long) Math.floor(minQ + diff / getMaxApparentPower() + * ess.maxNominalPower().valueOptional().orElse(0L)); + QEqualLimitation limit = new QEqualLimitation(ess.getPower()); + limit.setQ(power); + try { + ess.getPower().applyLimitation(limit); + reactivePower -= power; + } catch (PowerException e) { + log.error("Failed to set reactivePower on " + ess.id()); + } + } + } + } + +} diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java new file mode 100644 index 00000000000..ed28881e746 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java @@ -0,0 +1,128 @@ +package io.openems.core.utilities.power; + +import java.util.ArrayList; +import java.util.List; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.Point; + +import io.openems.api.bridge.BridgeEvent; +import io.openems.api.bridge.BridgeEventListener; +import io.openems.api.channel.WriteChannel; +import io.openems.api.exception.WriteChannelException;; + +public class SymmetricPowerImpl extends SymmetricPower implements LimitationChangedListener, BridgeEventListener { + /* + * Object + */ + + private WriteChannel setActivePower; + private WriteChannel setReactivePower; + private Geometry baseGeometry; + + private List staticLimitations; + private List dynamicLimitations; + + public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePower, + WriteChannel setReactivePower) { + setMaxApparentPower(maxApparentPower); + this.staticLimitations = new ArrayList<>(); + this.dynamicLimitations = new ArrayList<>(); + this.setActivePower = setActivePower; + this.setReactivePower = setReactivePower; + createBaseGeometry(); + reset(); + } + + public void addStaticLimitation(Limitation limit) { + this.staticLimitations.add(limit); + limit.addListener(this); + createBaseGeometry(); + } + + public void removeStaticLimitation(Limitation limit) { + limit.removeListener(this); + this.staticLimitations.remove(limit); + createBaseGeometry(); + } + + @Override + protected void reset() { + this.dynamicLimitations.clear(); + this.setGeometry(baseGeometry); + super.reset(); + } + + @Override + public void applyLimitation(Limitation limit) throws PowerException { + Geometry limitedPower = limit.applyLimit(getGeometry()); + if (!limitedPower.isEmpty()) { + setGeometry(limitedPower); + this.dynamicLimitations.add(limit); + } else { + throw new PowerException("No possible Power after applying Limit. Limit is not applied!"); + } + } + + private void writePower() { + Point p = reduceToZero(); + Coordinate c = p.getCoordinate(); + setGeometry(p); + try { + this.setActivePower.pushWrite((long) c.x); + this.setReactivePower.pushWrite((long) c.y); + setActivePower.shadowCopyAndReset(); + setReactivePower.shadowCopyAndReset(); + } catch (WriteChannelException e) { + log.error("failed to write Power.", e); + } + } + + private void createBaseGeometry() { + SHAPEFACTORY.setCentre(ZERO); + SHAPEFACTORY.setSize(getMaxApparentPower() * 2); + SHAPEFACTORY.setNumPoints(32); + this.baseGeometry = SHAPEFACTORY.createCircle(); + for (Limitation limit : this.staticLimitations) { + try { + Geometry limitedPower = limit.applyLimit(this.baseGeometry); + if (!limitedPower.isEmpty()) { + this.baseGeometry = limitedPower; + } else { + log.error("Power is empty after applying Limit. " + limit.toString()); + } + } catch (PowerException e) { + log.error("Failed to limit Power!", e); + } + } + } + + @Override + public void onLimitationChange(Limitation sender) { + if (staticLimitations.contains(sender)) { + createBaseGeometry(); + } + } + + @Override + public void onBridgeChange(BridgeEvent event) { + switch (event.getPosition()) { + case BEFOREREADOTHER1: + this.reset(); + break; + case BEFOREREADOTHER2: + break; + case BEFOREREADREQUIRED: + break; + case BEFOREWRITE: + this.writePower(); + + break; + default: + break; + + } + } + +} From d29c421e9b995d05115134aa16ed31baf79d9ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Feb 2018 10:51:42 +0100 Subject: [PATCH 034/156] add double array Parser --- .../io/openems/core/utilities/JsonUtils.java | 669 +++++++++--------- 1 file changed, 343 insertions(+), 326 deletions(-) diff --git a/edge/src/io/openems/core/utilities/JsonUtils.java b/edge/src/io/openems/core/utilities/JsonUtils.java index e2a2be4a390..d41ba049924 100644 --- a/edge/src/io/openems/core/utilities/JsonUtils.java +++ b/edge/src/io/openems/core/utilities/JsonUtils.java @@ -1,326 +1,343 @@ -/******************************************************************************* - * 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.core.utilities; - -import java.net.Inet4Address; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; - -import io.openems.api.exception.NotImplementedException; -import io.openems.api.exception.ReflectionException; - -public class JsonUtils { - public static JsonArray getAsJsonArray(JsonElement jElement) throws ReflectionException { - if (!jElement.isJsonArray()) { - throw new ReflectionException("Config is not a JsonArray: " + jElement); - } - return jElement.getAsJsonArray(); - }; - - public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws ReflectionException { - JsonElement jSubElement = getSubElement(jElement, memberName); - if (!jSubElement.isJsonArray()) { - throw new ReflectionException("Config [" + memberName + "] is not a JsonArray: " + jSubElement); - } - return jSubElement.getAsJsonArray(); - }; - - public static JsonObject getAsJsonObject(JsonElement jElement) throws ReflectionException { - if (!jElement.isJsonObject()) { - throw new ReflectionException("Config is not a JsonObject: " + jElement); - } - return jElement.getAsJsonObject(); - }; - - public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws ReflectionException { - JsonElement jsubElement = getSubElement(jElement, memberName); - if (!jsubElement.isJsonObject()) { - throw new ReflectionException("Config is not a JsonObject: " + jsubElement); - } - return jsubElement.getAsJsonObject(); - }; - - public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws ReflectionException { - if (!jElement.isJsonPrimitive()) { - throw new ReflectionException("Config is not a JsonPrimitive: " + jElement); - } - return jElement.getAsJsonPrimitive(); - } - - public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws ReflectionException { - JsonElement jSubElement = getSubElement(jElement, memberName); - if (!jSubElement.isJsonPrimitive()) { - throw new ReflectionException("Config is not a JsonPrimitive: " + jSubElement); - } - return jSubElement.getAsJsonPrimitive(); - } - - public static String getAsString(JsonElement jElement) throws ReflectionException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isString()) { - throw new ReflectionException("Config is not a String: " + jPrimitive); - } - return jPrimitive.getAsString(); - } - - public static String getAsString(JsonElement jElement, String memberName) throws ReflectionException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isString()) { - throw new ReflectionException("[" + memberName + "] is not a String: " + jPrimitive); - } - return jPrimitive.getAsString(); - } - - public static int getAsInt(JsonElement jElement, String memberName) throws ReflectionException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsInt(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Integer.parseInt(string); - } - throw new ReflectionException("[" + memberName + "] is not a Number: " + jPrimitive); - } - - public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone) - throws ReflectionException { - String[] date = JsonUtils.getAsString(jElement, memberName).split("-"); - try { - int year = Integer.valueOf(date[0]); - int month = Integer.valueOf(date[1]); - int day = Integer.valueOf(date[2]); - return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone); - } catch (ArrayIndexOutOfBoundsException e) { - throw new ReflectionException("Unable to parse date [" + memberName + "] from [" + jElement + "]: " + e); - } - } - - public static long getAsLong(JsonElement jElement, String memberName) throws ReflectionException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsLong(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Long.parseLong(string); - } - throw new ReflectionException("[" + memberName + "] is not a Number: " + jPrimitive); - } - - public static boolean getAsBoolean(JsonElement jElement, String memberName) throws ReflectionException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isBoolean()) { - return jPrimitive.getAsBoolean(); - } - throw new ReflectionException("[" + memberName + "] is not a Boolean: " + jPrimitive); - } - - public static JsonElement getSubElement(JsonElement jElement, String memberName) throws ReflectionException { - JsonObject jObject = getAsJsonObject(jElement); - if (!jObject.has(memberName)) { - throw new ReflectionException("[" + memberName + "] is missing: " + jElement); - } - return jObject.get(memberName); - } - - public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { - // null - if (value == null) { - return null; - } - // optional - if (value instanceof Optional) { - if (!((Optional) value).isPresent()) { - return null; - } else { - value = ((Optional) value).get(); - } - } - if (value instanceof Number) { - /* - * Number - */ - return new JsonPrimitive((Number) value); - } else if (value instanceof String) { - /* - * String - */ - return new JsonPrimitive((String) value); - } else if (value instanceof Boolean) { - /* - * Boolean - */ - return new JsonPrimitive((Boolean) value); - } else if (value instanceof Inet4Address) { - /* - * Inet4Address - */ - return new JsonPrimitive(((Inet4Address) value).getHostAddress()); - } else if (value instanceof JsonElement) { - /* - * JsonElement - */ - return (JsonElement) value; - } else if (value instanceof Long[]){ - /* - * Long-Array - */ - JsonArray js = new JsonArray(); - for (Long l : (Long[]) value){ - js.add(new JsonPrimitive((Long) l)); - } - return js; - } - throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // - + value.getClass().getSimpleName() + "]" // - + " to JSON is not implemented."); - } - - public static Object getAsType(Optional> typeOptional, JsonElement j) throws NotImplementedException { - if (!typeOptional.isPresent()) { - throw new NotImplementedException("Type of Channel was not set: " + j.getAsString()); - } - Class type = typeOptional.get(); - return getAsType(type, j); - } - - public static Object getAsType(Class type, JsonElement j) throws NotImplementedException { - try { - if (Integer.class.isAssignableFrom(type)) { - /* - * Asking for an Integer - */ - return j.getAsInt(); - - } else if (Long.class.isAssignableFrom(type)) { - /* - * Asking for an Long - */ - return j.getAsLong(); - } else if (Boolean.class.isAssignableFrom(type)) { - /* - * Asking for an Boolean - */ - return j.getAsBoolean(); - } else if (Double.class.isAssignableFrom(type)) { - /* - * Asking for an Double - */ - return j.getAsDouble(); - } else if (String.class.isAssignableFrom(type)) { - /* - * Asking for a String - */ - return j.getAsString(); - } else if (JsonObject.class.isAssignableFrom(type)) { - /* - * Asking for a JsonObject - */ - return j.getAsJsonObject(); - } else if (JsonArray.class.isAssignableFrom(type)) { - /* - * Asking for a JsonArray - */ - return j.getAsJsonArray(); - } else if (type.isArray()){ - /** - * Asking for Array - */ - if(Long.class.isAssignableFrom(type.getComponentType())){ - /** - * Asking for ArrayOfLong - */ - if(j.isJsonArray()){ - JsonArray js = j.getAsJsonArray(); - Long[] la = new Long[js.size()]; - for(int i = 0; i < js.size(); i++){ - la[i] = js.get(i).getAsLong(); - } - return la; - } - - } - } - } catch (IllegalStateException e) { - throw new IllegalStateException("Failed to parse JsonElement [" + j + "]", e); - } - throw new NotImplementedException( - "Converter for value [" + j + "] to class type [" + type + "] is not implemented."); - } - - public static boolean hasElement(JsonElement j, String... paths) { - return getMatchingElements(j, paths).size() > 0; - } - - public static Set getMatchingElements(JsonElement j, String... paths) { - Set result = new HashSet(); - if (paths.length == 0) { - // last path element - result.add(j); - return result; - } - String path = paths[0]; - if (j.isJsonObject()) { - JsonObject jO = j.getAsJsonObject(); - if (jO.has(path)) { - List nextPathsList = new ArrayList(Arrays.asList(paths)); - nextPathsList.remove(0); - String[] nextPaths = nextPathsList.toArray(new String[0]); - result.addAll(getMatchingElements(jO.get(path), nextPaths)); - } - } else if (j.isJsonArray()) { - for (JsonElement jE : j.getAsJsonArray()) { - result.addAll(getMatchingElements(jE, paths)); - } - } else if (j.isJsonPrimitive()) { - JsonPrimitive jP = j.getAsJsonPrimitive(); - if (jP.isString()) { - if (jP.getAsString().equals(path)) { - result.add(jP); - } - } - } - return result; - } - - /** - * Pretty print a JsonElement - * - * @param j - */ - public static void prettyPrint(JsonElement j) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(j); - System.out.println(json); - } -} +/******************************************************************************* + * 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.core.utilities; + +import java.net.Inet4Address; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import io.openems.api.exception.NotImplementedException; +import io.openems.api.exception.ReflectionException; + +public class JsonUtils { + public static JsonArray getAsJsonArray(JsonElement jElement) throws ReflectionException { + if (!jElement.isJsonArray()) { + throw new ReflectionException("Config is not a JsonArray: " + jElement); + } + return jElement.getAsJsonArray(); + }; + + public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws ReflectionException { + JsonElement jSubElement = getSubElement(jElement, memberName); + if (!jSubElement.isJsonArray()) { + throw new ReflectionException("Config [" + memberName + "] is not a JsonArray: " + jSubElement); + } + return jSubElement.getAsJsonArray(); + }; + + public static JsonObject getAsJsonObject(JsonElement jElement) throws ReflectionException { + if (!jElement.isJsonObject()) { + throw new ReflectionException("Config is not a JsonObject: " + jElement); + } + return jElement.getAsJsonObject(); + }; + + public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws ReflectionException { + JsonElement jsubElement = getSubElement(jElement, memberName); + if (!jsubElement.isJsonObject()) { + throw new ReflectionException("Config is not a JsonObject: " + jsubElement); + } + return jsubElement.getAsJsonObject(); + }; + + public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws ReflectionException { + if (!jElement.isJsonPrimitive()) { + throw new ReflectionException("Config is not a JsonPrimitive: " + jElement); + } + return jElement.getAsJsonPrimitive(); + } + + public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws ReflectionException { + JsonElement jSubElement = getSubElement(jElement, memberName); + if (!jSubElement.isJsonPrimitive()) { + throw new ReflectionException("Config is not a JsonPrimitive: " + jSubElement); + } + return jSubElement.getAsJsonPrimitive(); + } + + public static String getAsString(JsonElement jElement) throws ReflectionException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isString()) { + throw new ReflectionException("Config is not a String: " + jPrimitive); + } + return jPrimitive.getAsString(); + } + + public static String getAsString(JsonElement jElement, String memberName) throws ReflectionException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (!jPrimitive.isString()) { + throw new ReflectionException("[" + memberName + "] is not a String: " + jPrimitive); + } + return jPrimitive.getAsString(); + } + + public static int getAsInt(JsonElement jElement, String memberName) throws ReflectionException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsInt(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Integer.parseInt(string); + } + throw new ReflectionException("[" + memberName + "] is not a Number: " + jPrimitive); + } + + public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone) + throws ReflectionException { + String[] date = JsonUtils.getAsString(jElement, memberName).split("-"); + try { + int year = Integer.valueOf(date[0]); + int month = Integer.valueOf(date[1]); + int day = Integer.valueOf(date[2]); + return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone); + } catch (ArrayIndexOutOfBoundsException e) { + throw new ReflectionException("Unable to parse date [" + memberName + "] from [" + jElement + "]: " + e); + } + } + + public static long getAsLong(JsonElement jElement, String memberName) throws ReflectionException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsLong(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Long.parseLong(string); + } + throw new ReflectionException("[" + memberName + "] is not a Number: " + jPrimitive); + } + + public static boolean getAsBoolean(JsonElement jElement, String memberName) throws ReflectionException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isBoolean()) { + return jPrimitive.getAsBoolean(); + } + throw new ReflectionException("[" + memberName + "] is not a Boolean: " + jPrimitive); + } + + public static JsonElement getSubElement(JsonElement jElement, String memberName) throws ReflectionException { + JsonObject jObject = getAsJsonObject(jElement); + if (!jObject.has(memberName)) { + throw new ReflectionException("[" + memberName + "] is missing: " + jElement); + } + return jObject.get(memberName); + } + + public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { + // null + if (value == null) { + return null; + } + // optional + if (value instanceof Optional) { + if (!((Optional) value).isPresent()) { + return null; + } else { + value = ((Optional) value).get(); + } + } + if (value instanceof Number) { + /* + * Number + */ + return new JsonPrimitive((Number) value); + } else if (value instanceof String) { + /* + * String + */ + return new JsonPrimitive((String) value); + } else if (value instanceof Boolean) { + /* + * Boolean + */ + return new JsonPrimitive((Boolean) value); + } else if (value instanceof Inet4Address) { + /* + * Inet4Address + */ + return new JsonPrimitive(((Inet4Address) value).getHostAddress()); + } else if (value instanceof JsonElement) { + /* + * JsonElement + */ + return (JsonElement) value; + } else if (value instanceof Long[]){ + /* + * Long-Array + */ + JsonArray js = new JsonArray(); + for (Long l : (Long[]) value){ + js.add(new JsonPrimitive(l)); + } + return js; + } else if (value instanceof Double[]){ + /* + * Double-Array + */ + JsonArray js = new JsonArray(); + for (Double d : (Double[]) value){ + js.add(new JsonPrimitive(d)); + } + return js; + } + throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // + + value.getClass().getSimpleName() + "]" // + + " to JSON is not implemented."); + } + + public static Object getAsType(Optional> typeOptional, JsonElement j) throws NotImplementedException { + if (!typeOptional.isPresent()) { + throw new NotImplementedException("Type of Channel was not set: " + j.getAsString()); + } + Class type = typeOptional.get(); + return getAsType(type, j); + } + + public static Object getAsType(Class type, JsonElement j) throws NotImplementedException { + try { + if (Integer.class.isAssignableFrom(type)) { + /* + * Asking for an Integer + */ + return j.getAsInt(); + + } else if (Long.class.isAssignableFrom(type)) { + /* + * Asking for an Long + */ + return j.getAsLong(); + } else if (Boolean.class.isAssignableFrom(type)) { + /* + * Asking for an Boolean + */ + return j.getAsBoolean(); + } else if (Double.class.isAssignableFrom(type)) { + /* + * Asking for an Double + */ + return j.getAsDouble(); + } else if (String.class.isAssignableFrom(type)) { + /* + * Asking for a String + */ + return j.getAsString(); + } else if (JsonObject.class.isAssignableFrom(type)) { + /* + * Asking for a JsonObject + */ + return j.getAsJsonObject(); + } else if (JsonArray.class.isAssignableFrom(type)) { + /* + * Asking for a JsonArray + */ + return j.getAsJsonArray(); + } else if (type.isArray()){ + /** + * Asking for Array + */ + if(Long.class.isAssignableFrom(type.getComponentType())){ + /** + * Asking for ArrayOfLong + */ + if(j.isJsonArray()){ + JsonArray js = j.getAsJsonArray(); + Long[] la = new Long[js.size()]; + for(int i = 0; i < js.size(); i++){ + la[i] = js.get(i).getAsLong(); + } + return la; + } + + }else if(Double.class.isAssignableFrom(type.getComponentType())) { + if(j.isJsonArray()) { + JsonArray js = j.getAsJsonArray(); + Double[] da = new Double[js.size()]; + for(int i = 0; i < js.size(); i++) { + da[i] = js.get(i).getAsDouble(); + } + } + } + } + } catch (IllegalStateException e) { + throw new IllegalStateException("Failed to parse JsonElement [" + j + "]", e); + } + throw new NotImplementedException( + "Converter for value [" + j + "] to class type [" + type + "] is not implemented."); + } + + public static boolean hasElement(JsonElement j, String... paths) { + return getMatchingElements(j, paths).size() > 0; + } + + public static Set getMatchingElements(JsonElement j, String... paths) { + Set result = new HashSet(); + if (paths.length == 0) { + // last path element + result.add(j); + return result; + } + String path = paths[0]; + if (j.isJsonObject()) { + JsonObject jO = j.getAsJsonObject(); + if (jO.has(path)) { + List nextPathsList = new ArrayList(Arrays.asList(paths)); + nextPathsList.remove(0); + String[] nextPaths = nextPathsList.toArray(new String[0]); + result.addAll(getMatchingElements(jO.get(path), nextPaths)); + } + } else if (j.isJsonArray()) { + for (JsonElement jE : j.getAsJsonArray()) { + result.addAll(getMatchingElements(jE, paths)); + } + } else if (j.isJsonPrimitive()) { + JsonPrimitive jP = j.getAsJsonPrimitive(); + if (jP.isString()) { + if (jP.getAsString().equals(path)) { + result.add(jP); + } + } + } + return result; + } + + /** + * Pretty print a JsonElement + * + * @param j + */ + public static void prettyPrint(JsonElement j) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(j); + System.out.println(json); + } +} From 7fd0c7fe5e6e20fcfa8118b9551fe8839c715ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Feb 2018 10:52:29 +0100 Subject: [PATCH 035/156] add symmetricPower at symmetricEssNature --- .../device/nature/ess/SymmetricEssNature.java | 99 +-- .../commercial/FeneconCommercialEss.java | 733 ++---------------- .../impl/device/mini/FeneconMiniEss.java | 262 ++++--- .../io/openems/impl/device/refu/RefuEss.java | 336 ++++---- .../simulator/SimulatorSymmetricEss.java | 45 +- .../impl/device/sma/SunnyIsland6Ess.java | 45 +- .../UnitTestSymmetricEssNature.java | 19 +- 7 files changed, 497 insertions(+), 1042 deletions(-) diff --git a/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java b/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java index 58648211b8a..dbdcd227c63 100644 --- a/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java +++ b/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java @@ -1,49 +1,50 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.api.device.nature.ess; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.doc.ChannelInfo; - -public interface SymmetricEssNature extends EssNature { - /* - * ReadChannels - */ - @ChannelInfo(type = Long.class) - public ReadChannel activePower(); - - @ChannelInfo(type = Long.class) - public ReadChannel apparentPower(); - - @ChannelInfo(type = Long.class) - public ReadChannel reactivePower(); - - /* - * WriteChannels - */ - @ChannelInfo(type = Long.class) - public WriteChannel setActivePower(); - - @ChannelInfo(type = Long.class) - public WriteChannel setReactivePower(); - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.api.device.nature.ess; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.doc.ChannelInfo; +import io.openems.core.utilities.power.SymmetricPower; + +public interface SymmetricEssNature extends EssNature { + /* + * ReadChannels + */ + @ChannelInfo(type = Long.class) + public ReadChannel activePower(); + + @ChannelInfo(type = Long.class) + public ReadChannel apparentPower(); + + @ChannelInfo(type = Long.class) + public ReadChannel reactivePower(); + // /* + // * WriteChannels + // */ + // @ChannelInfo(type = Long.class) + // public WriteChannel setActivePower(); + // + // @ChannelInfo(type = Long.class) + // public WriteChannel setReactivePower(); + + public SymmetricPower getPower(); + +} diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java index 3d0162c6659..871a58282f8 100644 --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java @@ -20,6 +20,10 @@ *******************************************************************************/ package io.openems.impl.device.commercial; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; @@ -29,6 +33,11 @@ import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; +import io.openems.core.utilities.power.MaxCosPhiLimitation; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SMaxLimitation; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; @@ -90,6 +99,11 @@ public ConfigChannel chargeSoc() { private StaticValueChannel maxNominalPower = new StaticValueChannel<>("maxNominalPower", this, 40000L) .unit("VA"); private StaticValueChannel capacity = new StaticValueChannel<>("capacity", this, 40000L).unit("Wh"); + private SymmetricPowerImpl power; + private MaxCosPhiLimitation cosPhiLimit; + private PGreaterEqualLimitation allowedChargeLimit; + private PSmallerEqualLimitation allowedDischargeLimit; + private SMaxLimitation allowedApparentLimit; public StatusBitChannels warning; @Override @@ -132,16 +146,6 @@ public ModbusReadLongChannel systemState() { return systemState; } - @Override - public ModbusWriteLongChannel setActivePower() { - return setActivePower; - } - - @Override - public ModbusWriteLongChannel setReactivePower() { - return setReactivePower; - } - @Override public ModbusWriteLongChannel setWorkState() { return setWorkState; @@ -217,7 +221,7 @@ public ReadChannel maxNominalPower() { @Override protected ModbusProtocol defineModbusProtocol() throws ConfigException { warning = new StatusBitChannels("Warning", this); - return new ModbusProtocol( // + ModbusProtocol protocol = new ModbusProtocol( // new ModbusRegisterRange(0x0101, // new UnsignedWordElement(0x0101, // systemState = new ModbusReadLongChannel("SystemState", this) // @@ -530,7 +534,40 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new UnsignedWordElement(0x1404, batteryCellAverageTemperature = new ModbusReadLongChannel("BatteryCellAverageTemperature", this).unit("°C")) )); - + this.power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower); + this.cosPhiLimit = new MaxCosPhiLimitation(power); + this.cosPhiLimit.setMaxCosPhi(0.8); + this.power.addStaticLimitation(cosPhiLimit); + this.allowedApparentLimit = new SMaxLimitation(power); + this.allowedApparentLimit.setSMax(allowedApparent.valueOptional().orElse(0L), 0L, 0L); + this.allowedApparent.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedApparentLimit.setSMax(allowedApparent.valueOptional().orElse(0L), 0L, 0L); + } + }); + this.power.addStaticLimitation(this.allowedApparentLimit); + this.allowedChargeLimit = new PGreaterEqualLimitation(power); + this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); + this.allowedCharge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedChargeLimit.setP(allowedCharge.valueOptional().orElse(0L)); + } + }); + this.power.addStaticLimitation(this.allowedChargeLimit); + this.allowedDischargeLimit = new PSmallerEqualLimitation(power); + this.allowedDischargeLimit.setP(this.allowedDischarge.valueOptional().orElse(0L)); + this.allowedDischarge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedDischargeLimit.setP(allowedDischarge.valueOptional().orElse(0L)); + } + }); + return protocol; } @Override @@ -538,673 +575,9 @@ public StaticValueChannel capacity() { return capacity; } - // @IsChannel(id = "BatteryAccumulatedCharge") - // public final ModbusReadChannel _batteryAccumulatedCharge = new OldModbusChannelBuilder().nature(this).unit("Wh") - // .build(); - // @IsChannel(id = "BatteryAccumulatedDischarge") - // public final ModbusReadChannel _batteryAccumulatedDischarge = new - // OldModbusChannelBuilder().nature(this).unit("Wh") - // .build(); - // @IsChannel(id = "BatteryChargeCycles") - // public final ModbusReadChannel _batteryChargeCycles = new OldModbusChannelBuilder().nature(this).build(); - - // @IsChannel(id = "BatteryPower") - // public final ModbusReadChannel _batteryPower = new - // OldModbusChannelBuilder().nature(this).unit("W").multiplier(100) - // .build(); - // @IsChannel(id = "BatteryStringTotalCurrent") - // public final ModbusReadChannel _batteryStringTotalCurrent = new OldModbusChannelBuilder().nature(this).unit("mA") - // .multiplier(100).build(); - // @IsChannel(id = "BatteryStringAbnormity1") - // public final ModbusReadChannel _batteryStringAbnormity1 = new OldModbusChannelBuilder().nature(this) // - // .label(4, "Battery string voltage sampling route invalidation") // - // .label(16, "Battery string voltage sampling route disconnected") // - // .label(32, "Battery string temperature sampling route disconnected") // - // .label(64, "Battery string inside CAN disconnected") // - // .label(512, "Battery string current sampling circuit abnormity") // - // .label(1024, "Battery string battery cell invalidation") // - // .label(2048, "Battery string main contactor inspection abnormity") // - // .label(4096, "Battery string precharge contactor inspection abnormity") // - // .label(8192, "Battery string negative contactor inspection abnormity") // - // .label(16384, "Battery string power supply relay inspection abnormity")// - // .label(132768, "Battery string middle relay abnormity").build(); - // @IsChannel(id = "BatteryStringAbnormity2") - // public final ModbusReadChannel _batteryStringAbnormity2 = new OldModbusChannelBuilder().nature(this) // - // .label(4, "Battery string severe overtemperature") // - // .label(128, "Battery string smog fault") // - // .label(256, "Battery string blown fuse indicator fault") // - // .label(1024, "Battery string general leakage") // - // .label(2048, "Battery string severe leakage") // - // .label(4096, "Communication between BECU and periphery CAN disconnected") // - // .label(16384, "Battery string power supply relay contactor disconnected").build(); - // @IsChannel(id = "BatteryStringCellAverageTemperature") - // public final ModbusReadChannel _batteryStringCellAverageTemperature = new OldModbusChannelBuilder().nature(this) - // .unit("�C").multiplier(100).build(); - // @IsChannel(id = "BatteryStringChargeCurrentLimit") - // public final ModbusReadChannel _batteryStringChargeCurrentLimit = new OldModbusChannelBuilder().nature(this) - // .unit("mA").multiplier(100).build(); - // @IsChannel(id = "BatteryStringDischargeCurrentLimit") - // public final ModbusReadChannel _batteryStringDischargeCurrentLimit = new OldModbusChannelBuilder().nature(this) - // .unit("mA").multiplier(100).build(); - // @IsChannel(id = "BatteryStringPeripheralIoState") - // public final ModbusReadChannel _batteryStringPeripheralIoState = new OldModbusChannelBuilder().nature(this) - // .label(1, "Fuse state") // - // .label(2, "Isolated switch state").build(); - // @IsChannel(id = "BatteryStringSOH") - // public final ModbusReadChannel _batteryStringSOH = new OldModbusChannelBuilder().nature(this).unit("%") - // .multiplier(100).build(); - // @IsChannel(id = "BatteryStringSuggestiveInformation") - // public final ModbusReadChannel _batteryStringSuggestiveInformation = new OldModbusChannelBuilder().nature(this) - // .label(1, "Battery string charge general overcurrent") // - // .label(2, "Battery string discharge general overcurrent") // - // .label(4, "Battery string charge current over limit") // - // .label(8, "Battery string discharge current over limit") // - // .label(16, "Battery string general overvoltage") // - // .label(32, "Battery string general undervoltage") // - // .label(128, "Battery string general over temperature") // - // .label(256, "Battery string general under temperature") // - // .label(1024, "Battery string severe overvoltage") // - // .label(2048, "Battery string severe under voltage") // - // .label(4096, "Battery string severe under temperature") // - // .label(8192, "Battery string charge severe overcurrent") // - // .label(16384, "Battery string discharge severe overcurrent")// - // .label(132768, "Battery string capacity abnormity").build(); - - // @IsChannel(id = "BatteryStringTotalVoltage") - // public final ModbusReadChannel _batteryStringTotalVoltage = new OldModbusChannelBuilder().nature(this).unit("mV") - // .multiplier(100).build(); - // @IsChannel(id = "BatteryStringWorkState") - // public final ModbusReadChannel _batteryStringWorkState = new OldModbusChannelBuilder().nature(this) // - // .label(1, "Initial") // - // .label(2, "Stop") // - // .label(4, "Starting up") // - // .label(8, "Running") // - // .label(16, "Fault").build(); - - // private final OldConfigChannel _minSoc = new OldConfigChannelBuilder().nature(this).defaultValue(DEFAULT_MINSOC) - // .percentType().build(); - - // @IsChannel(id = "Abnormity1") - - // @IsChannel(id = "SwitchState") - // public final ModbusReadChannel _switchState = new OldModbusChannelBuilder().nature(this) // - // .label(2, "DC main contactor state") // - // .label(4, "DC precharge contactor state") // - // .label(8, "AC breaker state") // - // .label(16, "AC main contactor state") // - // .label(32, "AC precharge contactor state").build(); - - // @IsChannel(id = "TotalDateEnergy") - // public final ModbusReadChannel _totalDateEnergy = new OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalEnergy") - // public final ModbusReadChannel _totalEnergy = new OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy0") - // public final ModbusReadChannel _totalHourEnergy0 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy1") - // public final ModbusReadChannel _totalHourEnergy1 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy10") - // public final ModbusReadChannel _totalHourEnergy10 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy11") - // public final ModbusReadChannel _totalHourEnergy11 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy12") - // public final ModbusReadChannel _totalHourEnergy12 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy13") - // public final ModbusReadChannel _totalHourEnergy13 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy14") - // public final ModbusReadChannel _totalHourEnergy14 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy15") - // public final ModbusReadChannel _totalHourEnergy15 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy16") - // public final ModbusReadChannel _totalHourEnergy16 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy17") - // public final ModbusReadChannel _totalHourEnergy17 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy18") - // public final ModbusReadChannel _totalHourEnergy18 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy19") - // public final ModbusReadChannel _totalHourEnergy19 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy2") - // public final ModbusReadChannel _totalHourEnergy2 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy20") - // public final ModbusReadChannel _totalHourEnergy20 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy21") - // public final ModbusReadChannel _totalHourEnergy21 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy22") - // public final ModbusReadChannel _totalHourEnergy22 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy23") - // public final ModbusReadChannel _totalHourEnergy23 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy3") - // public final ModbusReadChannel _totalHourEnergy3 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy4") - // public final ModbusReadChannel _totalHourEnergy4 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy5") - // public final ModbusReadChannel _totalHourEnergy5 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy6") - // public final ModbusReadChannel _totalHourEnergy6 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy7") - // public final ModbusReadChannel _totalHourEnergy7 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy8") - // public final ModbusReadChannel _totalHourEnergy8 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalHourEnergy9") - // public final ModbusReadChannel _totalHourEnergy9 = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalMonthEnergy") - // public final ModbusReadChannel _totalMonthEnergy = new - // OldModbusChannelBuilder().nature(this).unit("kWh").build(); - // @IsChannel(id = "TotalYearEnergy") - // public final ModbusReadChannel _totalYearEnergy = new OldModbusChannelBuilder().nature(this).unit("kWh").build(); - - // @IsChannel(id = "MaxVoltageCellNo") - // public final ModbusReadChannel _maxVoltageCellNo = new OldModbusChannelBuilder().nature(this).build(); - // @IsChannel(id = "MaxVoltageCellVoltage") - // public final ModbusReadChannel _maxVoltageCellVoltage = new OldModbusChannelBuilder().nature(this).unit("mV") - // .build(); - // @IsChannel(id = "MaxVoltageCellTemp") - // public final ModbusReadChannel _maxVoltageCellTemp = new - // OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "MinVoltageCellNo") - // public final ModbusReadChannel _minVoltageCellNo = new OldModbusChannelBuilder().nature(this).build(); - // @IsChannel(id = "MinVoltageCellVoltage") - // public final ModbusReadChannel _minVoltageCellVoltage = new OldModbusChannelBuilder().nature(this).unit("mV") - // .build(); - // @IsChannel(id = "MinVoltageCellTemp") - // public final ModbusReadChannel _minVoltageCellTemp = new - // OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "MaxTempCellNo") - // public final ModbusReadChannel _maxTempCellNo = new OldModbusChannelBuilder().nature(this).build(); - // @IsChannel(id = "MaxTempCellVoltage") - // public final ModbusReadChannel _maxTempCellVoltage = new - // OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "MaxTempCellTemp") - // public final ModbusReadChannel _maxTempCellTemp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "MinTempCellNo") - // public final ModbusReadChannel _minTempCellNo = new OldModbusChannelBuilder().nature(this).build(); - // @IsChannel(id = "MinTempCellVoltage") - // public final ModbusReadChannel _minTempCellVoltage = new - // OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "MinTempCellTemp") - // public final ModbusReadChannel _minTempCellTemp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell1Voltage") - // public final ModbusReadChannel _cell1Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell2Voltage") - // public final ModbusReadChannel _cell2Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell3Voltage") - // public final ModbusReadChannel _cell3Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell4Voltage") - // public final ModbusReadChannel _cell4Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell5Voltage") - // public final ModbusReadChannel _cell5Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell6Voltage") - // public final ModbusReadChannel _cell6Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell7Voltage") - // public final ModbusReadChannel _cell7Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell8Voltage") - // public final ModbusReadChannel _cell8Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell9Voltage") - // public final ModbusReadChannel _cell9Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell10Voltage") - // public final ModbusReadChannel _cell10Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell11Voltage") - // public final ModbusReadChannel _cell11Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell12Voltage") - // public final ModbusReadChannel _cell12Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell13Voltage") - // public final ModbusReadChannel _cell13Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell14Voltage") - // public final ModbusReadChannel _cell14Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell15Voltage") - // public final ModbusReadChannel _cell15Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell16Voltage") - // public final ModbusReadChannel _cell16Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell17Voltage") - // public final ModbusReadChannel _cell17Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell18Voltage") - // public final ModbusReadChannel _cell18Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell19Voltage") - // public final ModbusReadChannel _cell19Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell20Voltage") - // public final ModbusReadChannel _cell20Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell21Voltage") - // public final ModbusReadChannel _cell21Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell22Voltage") - // public final ModbusReadChannel _cell22Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell23Voltage") - // public final ModbusReadChannel _cell23Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell24Voltage") - // public final ModbusReadChannel _cell24Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell25Voltage") - // public final ModbusReadChannel _cell25Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell26Voltage") - // public final ModbusReadChannel _cell26Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell27Voltage") - // public final ModbusReadChannel _cell27Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell28Voltage") - // public final ModbusReadChannel _cell28Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell29Voltage") - // public final ModbusReadChannel _cell29Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell30Voltage") - // public final ModbusReadChannel _cell30Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell31Voltage") - // public final ModbusReadChannel _cell31Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell32Voltage") - // public final ModbusReadChannel _cell32Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell33Voltage") - // public final ModbusReadChannel _cell33Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell34Voltage") - // public final ModbusReadChannel _cell34Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell35Voltage") - // public final ModbusReadChannel _cell35Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell36Voltage") - // public final ModbusReadChannel _cell36Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell37Voltage") - // public final ModbusReadChannel _cell37Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell38Voltage") - // public final ModbusReadChannel _cell38Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell39Voltage") - // public final ModbusReadChannel _cell39Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell40Voltage") - // public final ModbusReadChannel _cell40Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell41Voltage") - // public final ModbusReadChannel _cell41Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell42Voltage") - // public final ModbusReadChannel _cell42Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell43Voltage") - // public final ModbusReadChannel _cell43Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell44Voltage") - // public final ModbusReadChannel _cell44Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell45Voltage") - // public final ModbusReadChannel _cell45Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell46Voltage") - // public final ModbusReadChannel _cell46Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell47Voltage") - // public final ModbusReadChannel _cell47Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell48Voltage") - // public final ModbusReadChannel _cell48Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell49Voltage") - // public final ModbusReadChannel _cell49Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell50Voltage") - // public final ModbusReadChannel _cell50Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell51Voltage") - // public final ModbusReadChannel _cell51Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell52Voltage") - // public final ModbusReadChannel _cell52Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell53Voltage") - // public final ModbusReadChannel _cell53Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell54Voltage") - // public final ModbusReadChannel _cell54Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell55Voltage") - // public final ModbusReadChannel _cell55Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell56Voltage") - // public final ModbusReadChannel _cell56Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell57Voltage") - // public final ModbusReadChannel _cell57Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell58Voltage") - // public final ModbusReadChannel _cell58Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell59Voltage") - // public final ModbusReadChannel _cell59Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell60Voltage") - // public final ModbusReadChannel _cell60Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell61Voltage") - // public final ModbusReadChannel _cell61Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell62Voltage") - // public final ModbusReadChannel _cell62Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell63Voltage") - // public final ModbusReadChannel _cell63Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // @IsChannel(id = "Cell64Voltage") - // public final ModbusReadChannel _cell64Voltage = new OldModbusChannelBuilder().nature(this).unit("mV").build(); - // - // @IsChannel(id = "Cell1Temp") - // public final ModbusReadChannel _cell1Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell2Temp") - // public final ModbusReadChannel _cell2Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell3Temp") - // public final ModbusReadChannel _cell3Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell4Temp") - // public final ModbusReadChannel _cell4Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell5Temp") - // public final ModbusReadChannel _cell5Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell6Temp") - // public final ModbusReadChannel _cell6Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell7Temp") - // public final ModbusReadChannel _cell7Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell8Temp") - // public final ModbusReadChannel _cell8Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell9Temp") - // public final ModbusReadChannel _cell9Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell10Temp") - // public final ModbusReadChannel _cell10Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell11Temp") - // public final ModbusReadChannel _cell11Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell12Temp") - // public final ModbusReadChannel _cell12Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell13Temp") - // public final ModbusReadChannel _cell13Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell14Temp") - // public final ModbusReadChannel _cell14Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell15Temp") - // public final ModbusReadChannel _cell15Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell16Temp") - // public final ModbusReadChannel _cell16Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell17Temp") - // public final ModbusReadChannel _cell17Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell18Temp") - // public final ModbusReadChannel _cell18Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell19Temp") - // public final ModbusReadChannel _cell19Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell20Temp") - // public final ModbusReadChannel _cell20Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell21Temp") - // public final ModbusReadChannel _cell21Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell22Temp") - // public final ModbusReadChannel _cell22Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell23Temp") - // public final ModbusReadChannel _cell23Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell24Temp") - // public final ModbusReadChannel _cell24Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell25Temp") - // public final ModbusReadChannel _cell25Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell26Temp") - // public final ModbusReadChannel _cell26Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell27Temp") - // public final ModbusReadChannel _cell27Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell28Temp") - // public final ModbusReadChannel _cell28Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell29Temp") - // public final ModbusReadChannel _cell29Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell30Temp") - // public final ModbusReadChannel _cell30Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell31Temp") - // public final ModbusReadChannel _cell31Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell32Temp") - // public final ModbusReadChannel _cell32Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell33Temp") - // public final ModbusReadChannel _cell33Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell34Temp") - // public final ModbusReadChannel _cell34Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell35Temp") - // public final ModbusReadChannel _cell35Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell36Temp") - // public final ModbusReadChannel _cell36Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell37Temp") - // public final ModbusReadChannel _cell37Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell38Temp") - // public final ModbusReadChannel _cell38Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell39Temp") - // public final ModbusReadChannel _cell39Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell40Temp") - // public final ModbusReadChannel _cell40Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell41Temp") - // public final ModbusReadChannel _cell41Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell42Temp") - // public final ModbusReadChannel _cell42Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell43Temp") - // public final ModbusReadChannel _cell43Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell44Temp") - // public final ModbusReadChannel _cell44Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell45Temp") - // public final ModbusReadChannel _cell45Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell46Temp") - // public final ModbusReadChannel _cell46Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell47Temp") - // public final ModbusReadChannel _cell47Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell48Temp") - // public final ModbusReadChannel _cell48Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell49Temp") - // public final ModbusReadChannel _cell49Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell50Temp") - // public final ModbusReadChannel _cell50Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell51Temp") - // public final ModbusReadChannel _cell51Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell52Temp") - // public final ModbusReadChannel _cell52Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell53Temp") - // public final ModbusReadChannel _cell53Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell54Temp") - // public final ModbusReadChannel _cell54Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell55Temp") - // public final ModbusReadChannel _cell55Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell56Temp") - // public final ModbusReadChannel _cell56Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell57Temp") - // public final ModbusReadChannel _cell57Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell58Temp") - // public final ModbusReadChannel _cell58Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell59Temp") - // public final ModbusReadChannel _cell59Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell60Temp") - // public final ModbusReadChannel _cell60Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell61Temp") - // public final ModbusReadChannel _cell61Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell62Temp") - // public final ModbusReadChannel _cell62Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell63Temp") - // public final ModbusReadChannel _cell63Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - // @IsChannel(id = "Cell64Temp") - // public final ModbusReadChannel _cell64Temp = new OldModbusChannelBuilder().nature(this).unit("�C").build(); - - // @Override - // protected ModbusProtocol defineModbusProtocol() throws ConfigException { - // - - // new ModbusRange(0x0300, // - // new ElementBuilder().address(0x0300).channel(_totalEnergy).doubleword().build(), - // new ElementBuilder().address(0x0302).channel(_totalYearEnergy).doubleword().build(), - // new ElementBuilder().address(0x0304).channel(_totalMonthEnergy).doubleword().build(), - // new ElementBuilder().address(0x0306).channel(_totalDateEnergy).build(), - // new ElementBuilder().address(0x0307).channel(_totalHourEnergy0).build(), - // new ElementBuilder().address(0x0308).channel(_totalHourEnergy1).build(), - // new ElementBuilder().address(0x0309).channel(_totalHourEnergy2).build(), - // new ElementBuilder().address(0x030A).channel(_totalHourEnergy3).build(), - // new ElementBuilder().address(0x030B).channel(_totalHourEnergy4).build(), - // new ElementBuilder().address(0x030C).channel(_totalHourEnergy5).build(), - // new ElementBuilder().address(0x030D).channel(_totalHourEnergy6).build(), - // new ElementBuilder().address(0x030E).channel(_totalHourEnergy7).build(), - // new ElementBuilder().address(0x030F).channel(_totalHourEnergy8).build(), - // new ElementBuilder().address(0x0310).channel(_totalHourEnergy9).build(), - // new ElementBuilder().address(0x0311).channel(_totalHourEnergy10).build(), - // new ElementBuilder().address(0x0312).channel(_totalHourEnergy11).build(), - // new ElementBuilder().address(0x0313).channel(_totalHourEnergy12).build(), - // new ElementBuilder().address(0x0314).channel(_totalHourEnergy13).build(), - // new ElementBuilder().address(0x0315).channel(_totalHourEnergy14).build(), - // new ElementBuilder().address(0x0316).channel(_totalHourEnergy15).build(), - // new ElementBuilder().address(0x0317).channel(_totalHourEnergy16).build(), - // new ElementBuilder().address(0x0318).channel(_totalHourEnergy17).build(), - // new ElementBuilder().address(0x0319).channel(_totalHourEnergy18).build(), - // new ElementBuilder().address(0x031A).channel(_totalHourEnergy19).build(), - // new ElementBuilder().address(0x031B).channel(_totalHourEnergy20).build(), - // new ElementBuilder().address(0x031C).channel(_totalHourEnergy21).build(), - // new ElementBuilder().address(0x031D).channel(_totalHourEnergy22).build(), - // new ElementBuilder().address(0x031E).channel(_totalHourEnergy23).build()), + @Override + public SymmetricPowerImpl getPower() { + return power; + } - // new ModbusRange(0x1100, // - // new ElementBuilder().address(0x1100).channel(_batteryStringWorkState).build(), - // new ElementBuilder().address(0x1101).channel(_batteryStringSwitchState).build(), - // new ElementBuilder().address(0x1102).channel(_batteryStringPeripheralIoState).build(), - // new ElementBuilder().address(0x1103).channel(_batteryStringSuggestiveInformation).build(), - // new ElementBuilder().address(0x1104).dummy().build(), - // new ElementBuilder().address(0x1105).channel(_batteryStringAbnormity1).build(), - // new ElementBuilder().address(0x1106).channel(_batteryStringAbnormity2).build()), - // new ModbusRange(0x1400, // - // new ElementBuilder().address(0x1400).channel(_batteryStringTotalVoltage).build(), - // new ElementBuilder().address(0x1401).channel(_batteryStringTotalCurrent).signed().build(), - // new ElementBuilder().address(0x1402).channel(_soc).build(), - // new ElementBuilder().address(0x1403).channel(_batteryStringSOH).build(), - // new ElementBuilder().address(0x1404).channel(_batteryStringCellAverageTemperature).signed() - // .build(), - // new ElementBuilder().address(0x1405).dummy().build(), - // new ElementBuilder().address(0x1406).channel(_batteryStringChargeCurrentLimit).build(), - // new ElementBuilder().address(0x1407).channel(_batteryStringDischargeCurrentLimit).build(), - // new ElementBuilder().address(0x1408).dummy(0x140A - 0x1408).build(), - // new ElementBuilder().address(0x140A).channel(_batteryChargeCycles).doubleword().build(), - // new ElementBuilder().address(0x140C).dummy(0x1418 - 0x140C).build(), - // new ElementBuilder().address(0x1418).channel(_batteryAccumulatedCharge).doubleword().build(), - // new ElementBuilder().address(0x141A).channel(_batteryAccumulatedDischarge).doubleword().build(), - // new ElementBuilder().address(0x141C).dummy(0x1420 - 0x141C).build(), - // new ElementBuilder().address(0x1420).channel(_batteryPower).signed().build(), - // new ElementBuilder().address(0x1421).dummy(0x1430 - 0x1421).build(), - // new ElementBuilder().address(0x1430).channel(_maxVoltageCellNo).build(), - // new ElementBuilder().address(0x1431).channel(_maxVoltageCellVoltage).build(), - // new ElementBuilder().address(0x1432).channel(_maxVoltageCellTemp).signed().build(), - // new ElementBuilder().address(0x1433).channel(_minVoltageCellNo).build(), - // new ElementBuilder().address(0x1434).channel(_minVoltageCellVoltage).build(), - // new ElementBuilder().address(0x1435).channel(_minVoltageCellTemp).signed().build(), - // new ElementBuilder().address(0x1436).dummy(0x143A - 0x1436).build(), - // new ElementBuilder().address(0x143A).channel(_maxTempCellNo).build(), - // new ElementBuilder().address(0x143B).channel(_maxTempCellTemp).signed().build(), - // new ElementBuilder().address(0x143C).channel(_maxTempCellVoltage).build(), - // new ElementBuilder().address(0x143D).channel(_minTempCellNo).build(), - // new ElementBuilder().address(0x143E).channel(_minTempCellTemp).signed().build(), - // new ElementBuilder().address(0x143F).channel(_minTempCellVoltage).build()), // - // new ModbusRange(0x1500, new ElementBuilder().address(0x1500).channel(_cell1Voltage).build(), - // new ElementBuilder().address(0x1501).channel(_cell2Voltage).build(), - // new ElementBuilder().address(0x1502).channel(_cell3Voltage).build(), - // new ElementBuilder().address(0x1503).channel(_cell4Voltage).build(), - // new ElementBuilder().address(0x1504).channel(_cell5Voltage).build(), - // new ElementBuilder().address(0x1505).channel(_cell6Voltage).build(), - // new ElementBuilder().address(0x1506).channel(_cell7Voltage).build(), - // new ElementBuilder().address(0x1507).channel(_cell8Voltage).build(), - // new ElementBuilder().address(0x1508).channel(_cell9Voltage).build(), - // new ElementBuilder().address(0x1509).channel(_cell10Voltage).build(), - // new ElementBuilder().address(0x150a).channel(_cell11Voltage).build(), - // new ElementBuilder().address(0x150b).channel(_cell12Voltage).build(), - // new ElementBuilder().address(0x150c).channel(_cell13Voltage).build(), - // new ElementBuilder().address(0x150d).channel(_cell14Voltage).build(), - // new ElementBuilder().address(0x150e).channel(_cell15Voltage).build(), - // new ElementBuilder().address(0x150f).channel(_cell16Voltage).build(), - // new ElementBuilder().address(0x1510).channel(_cell17Voltage).build(), - // new ElementBuilder().address(0x1511).channel(_cell18Voltage).build(), - // new ElementBuilder().address(0x1512).channel(_cell19Voltage).build(), - // new ElementBuilder().address(0x1513).channel(_cell20Voltage).build(), - // new ElementBuilder().address(0x1514).channel(_cell21Voltage).build(), - // new ElementBuilder().address(0x1515).channel(_cell22Voltage).build(), - // new ElementBuilder().address(0x1516).channel(_cell23Voltage).build(), - // new ElementBuilder().address(0x1517).channel(_cell24Voltage).build(), - // new ElementBuilder().address(0x1518).channel(_cell25Voltage).build(), - // new ElementBuilder().address(0x1519).channel(_cell26Voltage).build(), - // new ElementBuilder().address(0x151a).channel(_cell27Voltage).build(), - // new ElementBuilder().address(0x151b).channel(_cell28Voltage).build(), - // new ElementBuilder().address(0x151c).channel(_cell29Voltage).build(), - // new ElementBuilder().address(0x151d).channel(_cell30Voltage).build(), - // new ElementBuilder().address(0x151e).channel(_cell31Voltage).build(), - // new ElementBuilder().address(0x151f).channel(_cell32Voltage).build(), - // new ElementBuilder().address(0x1520).channel(_cell33Voltage).build(), - // new ElementBuilder().address(0x1521).channel(_cell34Voltage).build(), - // new ElementBuilder().address(0x1522).channel(_cell35Voltage).build(), - // new ElementBuilder().address(0x1523).channel(_cell36Voltage).build(), - // new ElementBuilder().address(0x1524).channel(_cell37Voltage).build(), - // new ElementBuilder().address(0x1525).channel(_cell38Voltage).build(), - // new ElementBuilder().address(0x1526).channel(_cell39Voltage).build(), - // new ElementBuilder().address(0x1527).channel(_cell40Voltage).build(), - // new ElementBuilder().address(0x1528).channel(_cell41Voltage).build(), - // new ElementBuilder().address(0x1529).channel(_cell42Voltage).build(), - // new ElementBuilder().address(0x152a).channel(_cell43Voltage).build(), - // new ElementBuilder().address(0x152b).channel(_cell44Voltage).build(), - // new ElementBuilder().address(0x152c).channel(_cell45Voltage).build(), - // new ElementBuilder().address(0x152d).channel(_cell46Voltage).build(), - // new ElementBuilder().address(0x152e).channel(_cell47Voltage).build(), - // new ElementBuilder().address(0x152f).channel(_cell48Voltage).build(), - // new ElementBuilder().address(0x1530).channel(_cell49Voltage).build(), - // new ElementBuilder().address(0x1531).channel(_cell50Voltage).build(), - // new ElementBuilder().address(0x1532).channel(_cell51Voltage).build(), - // new ElementBuilder().address(0x1533).channel(_cell52Voltage).build(), - // new ElementBuilder().address(0x1534).channel(_cell53Voltage).build(), - // new ElementBuilder().address(0x1535).channel(_cell54Voltage).build(), - // new ElementBuilder().address(0x1536).channel(_cell55Voltage).build(), - // new ElementBuilder().address(0x1537).channel(_cell56Voltage).build(), - // new ElementBuilder().address(0x1538).channel(_cell57Voltage).build(), - // new ElementBuilder().address(0x1539).channel(_cell58Voltage).build(), - // new ElementBuilder().address(0x153a).channel(_cell59Voltage).build(), - // new ElementBuilder().address(0x153b).channel(_cell60Voltage).build(), - // new ElementBuilder().address(0x153c).channel(_cell61Voltage).build(), - // new ElementBuilder().address(0x153d).channel(_cell62Voltage).build(), - // new ElementBuilder().address(0x153e).channel(_cell63Voltage).build(), - // new ElementBuilder().address(0x153f).channel(_cell64Voltage).build()), - // new ModbusRange(0x1700, // - // new ElementBuilder().address(0x1700).channel(_cell1Temp).build(), - // new ElementBuilder().address(0x1701).channel(_cell2Temp).build(), - // new ElementBuilder().address(0x1702).channel(_cell3Temp).build(), - // new ElementBuilder().address(0x1703).channel(_cell4Temp).build(), - // new ElementBuilder().address(0x1704).channel(_cell5Temp).build(), - // new ElementBuilder().address(0x1705).channel(_cell6Temp).build(), - // new ElementBuilder().address(0x1706).channel(_cell7Temp).build(), - // new ElementBuilder().address(0x1707).channel(_cell8Temp).build(), - // new ElementBuilder().address(0x1708).channel(_cell9Temp).build(), - // new ElementBuilder().address(0x1709).channel(_cell10Temp).build(), - // new ElementBuilder().address(0x170a).channel(_cell11Temp).build(), - // new ElementBuilder().address(0x170b).channel(_cell12Temp).build(), - // new ElementBuilder().address(0x170c).channel(_cell13Temp).build(), - // new ElementBuilder().address(0x170d).channel(_cell14Temp).build(), - // new ElementBuilder().address(0x170e).channel(_cell15Temp).build(), - // new ElementBuilder().address(0x170f).channel(_cell16Temp).build(), - // new ElementBuilder().address(0x1710).channel(_cell17Temp).build(), - // new ElementBuilder().address(0x1711).channel(_cell18Temp).build(), - // new ElementBuilder().address(0x1712).channel(_cell19Temp).build(), - // new ElementBuilder().address(0x1713).channel(_cell20Temp).build(), - // new ElementBuilder().address(0x1714).channel(_cell21Temp).build(), - // new ElementBuilder().address(0x1715).channel(_cell22Temp).build(), - // new ElementBuilder().address(0x1716).channel(_cell23Temp).build(), - // new ElementBuilder().address(0x1717).channel(_cell24Temp).build(), - // new ElementBuilder().address(0x1718).channel(_cell25Temp).build(), - // new ElementBuilder().address(0x1719).channel(_cell26Temp).build(), - // new ElementBuilder().address(0x171a).channel(_cell27Temp).build(), - // new ElementBuilder().address(0x171b).channel(_cell28Temp).build(), - // new ElementBuilder().address(0x171c).channel(_cell29Temp).build(), - // new ElementBuilder().address(0x171d).channel(_cell30Temp).build(), - // new ElementBuilder().address(0x171e).channel(_cell31Temp).build(), - // new ElementBuilder().address(0x171f).channel(_cell32Temp).build(), - // new ElementBuilder().address(0x1720).channel(_cell33Temp).build(), - // new ElementBuilder().address(0x1721).channel(_cell34Temp).build(), - // new ElementBuilder().address(0x1722).channel(_cell35Temp).build(), - // new ElementBuilder().address(0x1723).channel(_cell36Temp).build(), - // new ElementBuilder().address(0x1724).channel(_cell37Temp).build(), - // new ElementBuilder().address(0x1725).channel(_cell38Temp).build(), - // new ElementBuilder().address(0x1726).channel(_cell39Temp).build(), - // new ElementBuilder().address(0x1727).channel(_cell40Temp).build(), - // new ElementBuilder().address(0x1728).channel(_cell41Temp).build(), - // new ElementBuilder().address(0x1729).channel(_cell42Temp).build(), - // new ElementBuilder().address(0x172a).channel(_cell43Temp).build(), - // new ElementBuilder().address(0x172b).channel(_cell44Temp).build(), - // new ElementBuilder().address(0x172c).channel(_cell45Temp).build(), - // new ElementBuilder().address(0x172d).channel(_cell46Temp).build(), - // new ElementBuilder().address(0x172e).channel(_cell47Temp).build(), - // new ElementBuilder().address(0x172f).channel(_cell48Temp).build(), - // new ElementBuilder().address(0x1730).channel(_cell49Temp).build(), - // new ElementBuilder().address(0x1731).channel(_cell50Temp).build(), - // new ElementBuilder().address(0x1732).channel(_cell51Temp).build(), - // new ElementBuilder().address(0x1733).channel(_cell52Temp).build(), - // new ElementBuilder().address(0x1734).channel(_cell53Temp).build(), - // new ElementBuilder().address(0x1735).channel(_cell54Temp).build(), - // new ElementBuilder().address(0x1736).channel(_cell55Temp).build(), - // new ElementBuilder().address(0x1737).channel(_cell56Temp).build(), - // new ElementBuilder().address(0x1738).channel(_cell57Temp).build(), - // new ElementBuilder().address(0x1739).channel(_cell58Temp).build(), - // new ElementBuilder().address(0x173a).channel(_cell59Temp).build(), - // new ElementBuilder().address(0x173b).channel(_cell60Temp).build(), - // new ElementBuilder().address(0x173c).channel(_cell61Temp).build(), - // new ElementBuilder().address(0x173d).channel(_cell62Temp).build(), - // new ElementBuilder().address(0x173e).channel(_cell63Temp).build(), - // new ElementBuilder().address(0x173f).channel(_cell64Temp).build())); - // } } diff --git a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java index ffcf2de55af..fdc41372186 100644 --- a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java +++ b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java @@ -20,6 +20,10 @@ *******************************************************************************/ package io.openems.impl.device.mini; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.FunctionalReadChannel; import io.openems.api.channel.ReadChannel; @@ -35,6 +39,10 @@ import io.openems.api.exception.ConfigException; import io.openems.api.exception.InvalidValueException; import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SMaxLimitation; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; @@ -104,6 +112,10 @@ public ConfigChannel chargeSoc() { private StaticValueChannel nominalPower = new StaticValueChannel("maxNominalPower", this, 3000l) .unit("VA"); private StaticValueChannel capacity = new StaticValueChannel<>("capacity", this, 3000L).unit("Wh"); + private SymmetricPowerImpl power; + private PGreaterEqualLimitation allowedChargeLimit; + private PSmallerEqualLimitation allowedDischargeLimit; + private SMaxLimitation allowedApparentLimit; @Override public ReadChannel allowedCharge() { @@ -140,16 +152,6 @@ public ReadChannel activePower() { return activePower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - @Override public StatusBitChannels warning() { return warning; @@ -231,30 +233,30 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { ModbusProtocol protokol = new ModbusProtocol(new ModbusRegisterRange(100, // new UnsignedWordElement(100, // systemState = new ModbusReadLongChannel("SystemState", this) // - .label(0, STANDBY) // - .label(1, "Start Off-Grid") // - .label(2, START) // - .label(3, FAULT) // - .label(4, "Off-grid PV")), + .label(0, STANDBY) // + .label(1, "Start Off-Grid") // + .label(2, START) // + .label(3, FAULT) // + .label(4, "Off-grid PV")), new UnsignedWordElement(101, // controlMode = new ModbusReadLongChannel("ControlMode", this) // - .label(1, "Remote") // - .label(2, "Local")), // + .label(1, "Remote") // + .label(2, "Local")), // new DummyElement(102, 103), // new UnsignedDoublewordElement(104, // totalBatteryChargeEnergy = new ModbusReadLongChannel("TotalBatteryChargeEnergy", this) - .unit("Wh")), // + .unit("Wh")), // new UnsignedDoublewordElement(106, // totalBatteryDischargeEnergy = new ModbusReadLongChannel("TotalBatteryDischargeEnergy", this) - .unit("Wh")), // + .unit("Wh")), // new UnsignedWordElement(108, // batteryGroupState = new ModbusReadLongChannel("BatteryGroupState", this) // - .label(0, "Initial") // - .label(1, "Stop") // - .label(2, "Starting") // - .label(3, "Running") // - .label(4, "Stopping") // - .label(5, "Fail")), + .label(0, "Initial") // + .label(1, "Stop") // + .label(2, "Starting") // + .label(3, "Running") // + .label(4, "Stopping") // + .label(5, "Fail")), new UnsignedWordElement(109, // soc = new ModbusReadLongChannel("Soc", this).unit("%").interval(0, 100)), new UnsignedWordElement(110, // @@ -265,23 +267,23 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { batteryPower = new ModbusReadLongChannel("BatteryPower", this).unit("W")), new UnsignedWordElement(113, // batteryGroupAlarm = new ModbusReadLongChannel("BatteryGroupAlarm", this) - .label(1, "Fail, The system should be stopped") // - .label(2, "Common low voltage alarm") // - .label(4, "Common high voltage alarm") // - .label(8, "Charging over current alarm") // - .label(16, "Discharging over current alarm") // - .label(32, "Over temperature alarm")// - .label(64, "Interal communication abnormal")), + .label(1, "Fail, The system should be stopped") // + .label(2, "Common low voltage alarm") // + .label(4, "Common high voltage alarm") // + .label(8, "Charging over current alarm") // + .label(16, "Discharging over current alarm") // + .label(32, "Over temperature alarm")// + .label(64, "Interal communication abnormal")), new UnsignedWordElement(114, // pcsOperationState = new ModbusReadLongChannel("PcsOperationState", this) - .label(0, "Self-checking") // - .label(1, "Standby") // - .label(2, "Off grid PV") // - .label(3, "Off grid") // - .label(4, ON_GRID) // - .label(5, "Fail") // - .label(6, "bypass 1") // - .label(7, "bypass 2")), + .label(0, "Self-checking") // + .label(1, "Standby") // + .label(2, "Off grid PV") // + .label(3, "Off grid") // + .label(4, ON_GRID) // + .label(5, "Fail") // + .label(6, "bypass 1") // + .label(7, "bypass 2")), new DummyElement(115, 117), // new SignedWordElement(118, // current = new ModbusReadLongChannel("Current", this).unit("mA").multiplier(2)), @@ -312,63 +314,63 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .label(256, "Combination error")// .label(512, "Comm with inverter error")// .label(1024, "Tme error")// - )), new UnsignedWordElement(151, pcsAlarm2 = warning.channel(new StatusBitChannel("PcsAlarm2", this)// - )), new UnsignedWordElement(152, warning.channel(pcsFault1 = new StatusBitChannel("PcsFault1", this)// - .label(1, "Control current overload 100%")// - .label(2, "Control current overload 110%")// - .label(4, "Control current overload 150%")// - .label(8, "Control current overload 200%")// - .label(16, "Control current overload 120%")// - .label(32, "Control current overload 300%")// - .label(64, "Control transient load 300%")// - .label(128, "Grid over current")// - .label(256, "Locking waveform too many times")// - .label(512, "Inverter voltage zero drift error")// - .label(1024, "Grid voltage zero drift error")// - .label(2048, "Control current zero drift error")// - .label(4096, "Inverter current zero drift error")// - .label(8192, "Grid current zero drift error")// - .label(16384, "PDP protection")// - .label(32768, "Hardware control current protection")// - )), new UnsignedWordElement(153, warning.channel(pcsFault2 = new StatusBitChannel("PcsFault2", this)// - .label(1, "Hardware AC volt. protection")// - .label(2, "Hardware DC curr. protection")// - .label(4, "Hardware temperature protection")// - .label(8, "No capturing signal")// - .label(16, "DC overvoltage")// - .label(32, "DC disconnected")// - .label(64, "Inverter undervoltage")// - .label(128, "Inverter overvoltage")// - .label(256, "Current sensor fail")// - .label(512, "Voltage sensor fail")// - .label(1024, "Power uncontrollable")// - .label(2048, "Current uncontrollable")// - .label(4096, "Fan error")// - .label(8192, "Phase lack")// - .label(16384, "Inverter relay fault")// - .label(32768, "Grid relay fault")// - )), new UnsignedWordElement(154, warning.channel(pcsFault3 = new StatusBitChannel("PcsFault3", this)// - .label(1, "Control panel overtemp")// - .label(2, "Power panel overtemp")// - .label(4, "DC input overcurrent")// - .label(8, "Capacitor overtemp")// - .label(16, "Radiator overtemp")// - .label(32, "Transformer overtemp")// - .label(64, "Combination comm error")// - .label(128, "EEPROM error")// - .label(256, "Load current zero drift error")// - .label(512, "Current limit-R error")// - .label(1024, "Phase sync error")// - .label(2048, "External PV current zero drift error")// - .label(4096, "External grid current zero drift error")// - ))), // + )), new UnsignedWordElement(151, pcsAlarm2 = warning.channel(new StatusBitChannel("PcsAlarm2", this)// + )), new UnsignedWordElement(152, warning.channel(pcsFault1 = new StatusBitChannel("PcsFault1", this)// + .label(1, "Control current overload 100%")// + .label(2, "Control current overload 110%")// + .label(4, "Control current overload 150%")// + .label(8, "Control current overload 200%")// + .label(16, "Control current overload 120%")// + .label(32, "Control current overload 300%")// + .label(64, "Control transient load 300%")// + .label(128, "Grid over current")// + .label(256, "Locking waveform too many times")// + .label(512, "Inverter voltage zero drift error")// + .label(1024, "Grid voltage zero drift error")// + .label(2048, "Control current zero drift error")// + .label(4096, "Inverter current zero drift error")// + .label(8192, "Grid current zero drift error")// + .label(16384, "PDP protection")// + .label(32768, "Hardware control current protection")// + )), new UnsignedWordElement(153, warning.channel(pcsFault2 = new StatusBitChannel("PcsFault2", this)// + .label(1, "Hardware AC volt. protection")// + .label(2, "Hardware DC curr. protection")// + .label(4, "Hardware temperature protection")// + .label(8, "No capturing signal")// + .label(16, "DC overvoltage")// + .label(32, "DC disconnected")// + .label(64, "Inverter undervoltage")// + .label(128, "Inverter overvoltage")// + .label(256, "Current sensor fail")// + .label(512, "Voltage sensor fail")// + .label(1024, "Power uncontrollable")// + .label(2048, "Current uncontrollable")// + .label(4096, "Fan error")// + .label(8192, "Phase lack")// + .label(16384, "Inverter relay fault")// + .label(32768, "Grid relay fault")// + )), new UnsignedWordElement(154, warning.channel(pcsFault3 = new StatusBitChannel("PcsFault3", this)// + .label(1, "Control panel overtemp")// + .label(2, "Power panel overtemp")// + .label(4, "DC input overcurrent")// + .label(8, "Capacitor overtemp")// + .label(16, "Radiator overtemp")// + .label(32, "Transformer overtemp")// + .label(64, "Combination comm error")// + .label(128, "EEPROM error")// + .label(256, "Load current zero drift error")// + .label(512, "Current limit-R error")// + .label(1024, "Phase sync error")// + .label(2048, "External PV current zero drift error")// + .label(4096, "External grid current zero drift error")// + ))), // new WriteableModbusRegisterRange(200, // new UnsignedWordElement(200, setWorkState = new ModbusWriteLongChannel("SetWorkState", this)// - .label(0, "Local control") // - .label(1, START) // "Remote control on grid starting" - .label(2, "Remote control off grid starting") // - .label(3, STOP)// - .label(4, "Emergency Stop"))), + .label(0, "Local control") // + .label(1, START) // "Remote control on grid starting" + .label(2, "Remote control off grid starting") // + .label(3, STOP)// + .label(4, "Emergency Stop"))), new WriteableModbusRegisterRange(201, // new SignedWordElement(201, setActivePower = new ModbusWriteLongChannel("SetActivePower", this).unit("W")), // @@ -384,28 +386,28 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new WriteableModbusRegisterRange(30558, new UnsignedWordElement(30558, setSetupMode = new ModbusWriteLongChannel("SetSetupMode", this).label(0, EssNature.OFF) - .label(1, EssNature.ON))), + .label(1, EssNature.ON))), new WriteableModbusRegisterRange(30559, new UnsignedWordElement(30559, setPcsMode = new ModbusWriteLongChannel("SetPcsMode", this)// - .label(0, "Emergency")// - .label(1, "ConsumersPeakPattern")// - .label(2, "Economic")// - .label(3, "Eco")// - .label(4, "Debug")// - .label(5, "SmoothPv")// - .label(6, "Remote"))), + .label(0, "Emergency")// + .label(1, "ConsumersPeakPattern")// + .label(2, "Economic")// + .label(3, "Eco")// + .label(4, "Debug")// + .label(5, "SmoothPv")// + .label(6, "Remote"))), new ModbusRegisterRange(30157, new UnsignedWordElement(30157, setupMode = new ModbusReadLongChannel("SetupMode", this)// - .label(0, EssNature.OFF)// - .label(1, EssNature.ON)), + .label(0, EssNature.OFF)// + .label(1, EssNature.ON)), new UnsignedWordElement(30158, pcsMode = new ModbusReadLongChannel("PcsMode", this)// - .label(0, "Emergency")// - .label(1, "ConsumersPeakPattern")// - .label(2, "Economic")// - .label(3, "Eco")// - .label(4, "Debug")// - .label(5, "SmoothPv")// - .label(6, "Remote")))); + .label(0, "Emergency")// + .label(1, "ConsumersPeakPattern")// + .label(2, "Economic")// + .label(3, "Eco")// + .label(4, "Debug")// + .label(5, "SmoothPv")// + .label(6, "Remote")))); gridMode = new FunctionalReadChannel("GridMode", this, (channels) -> { ReadChannel state = channels[0]; try { @@ -428,7 +430,36 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { } return 0l; }, activePower, reactivePower); - + this.power = new SymmetricPowerImpl(3000, setActivePower, setReactivePower); + this.allowedApparentLimit = new SMaxLimitation(power); + this.allowedApparentLimit.setSMax(phaseAllowedApparent.valueOptional().orElse(0L)*3, 0L, 0L); + this.phaseAllowedApparent.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedApparentLimit.setSMax(phaseAllowedApparent.valueOptional().orElse(0L)*3, 0L, 0L); + } + }); + this.power.addStaticLimitation(this.allowedApparentLimit); + this.allowedChargeLimit = new PGreaterEqualLimitation(power); + this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); + this.allowedCharge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedChargeLimit.setP(allowedCharge.valueOptional().orElse(0L)); + } + }); + this.power.addStaticLimitation(this.allowedChargeLimit); + this.allowedDischargeLimit = new PSmallerEqualLimitation(power); + this.allowedDischargeLimit.setP(this.allowedDischarge.valueOptional().orElse(0L)); + this.allowedDischarge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedDischargeLimit.setP(allowedDischarge.valueOptional().orElse(0L)); + } + }); return protokol; } @@ -447,4 +478,9 @@ public StaticValueChannel capacity() { return capacity; } + @Override + public SymmetricPowerImpl getPower() { + return power; + } + } diff --git a/edge/src/io/openems/impl/device/refu/RefuEss.java b/edge/src/io/openems/impl/device/refu/RefuEss.java index 7170ff20463..3a79ca52090 100644 --- a/edge/src/io/openems/impl/device/refu/RefuEss.java +++ b/edge/src/io/openems/impl/device/refu/RefuEss.java @@ -20,6 +20,10 @@ *******************************************************************************/ package io.openems.impl.device.refu; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; @@ -31,6 +35,10 @@ import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; +import io.openems.core.utilities.power.NoPBetweenLimitation; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; @@ -100,6 +108,11 @@ public ConfigChannel chargeSoc() { private StaticValueChannel maxNominalPower = new StaticValueChannel<>("maxNominalPower", this, 100000L) .unit("VA").unit("VA"); private StaticValueChannel capacity = new StaticValueChannel<>("capacity", this, 130000L).unit("Wh"); + private SymmetricPowerImpl power; + private PGreaterEqualLimitation allowedChargeLimit; + private PSmallerEqualLimitation allowedDischargeLimit; + private NoPBetweenLimitation batFullLimit; + private NoPBetweenLimitation batEmptyLimit; public StatusBitChannels warning; /* @@ -245,29 +258,19 @@ public ReadChannel maxNominalPower() { return maxNominalPower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - @Override protected ModbusProtocol defineModbusProtocol() throws ConfigException { warning = new StatusBitChannels("Warning", this); - return new ModbusProtocol( // + ModbusProtocol protocol = new ModbusProtocol( // new ModbusInputRegisterRange(0x100, // new UnsignedWordElement(0x100, // systemState = new ModbusReadLongChannel("SystemState", this) // - .label(0, STOP) // - .label(1, "Init") // - .label(2, "Pre-operation") // - .label(3, STANDBY) // - .label(4, START) // - .label(5, FAULT)), + .label(0, STOP) // + .label(1, "Init") // + .label(2, "Pre-operation") // + .label(3, STANDBY) // + .label(4, START) // + .label(5, FAULT)), new UnsignedWordElement(0x101, systemError1 = warning.channel(new StatusBitChannel("SystemError1", this)// .label(1, "BMS In Error")// @@ -281,15 +284,15 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .label(256, "Overcurrent warning")// .label(512, "BMS Ready")// .label(1024, "TREX Ready")// - )), + )), new UnsignedWordElement(0x102, communicationInformations = new StatusBitChannel("CommunicationInformations", this)// - .label(1, "Gateway Initialized")// - .label(2, "Modbus Slave Status")// - .label(4, "Modbus Master Status")// - .label(8, "CAN Timeout")// - .label(16, "First Communication Ok")// - ), new UnsignedWordElement(0x103, inverterStatus = new StatusBitChannel("InverterStatus", this)// + .label(1, "Gateway Initialized")// + .label(2, "Modbus Slave Status")// + .label(4, "Modbus Master Status")// + .label(8, "CAN Timeout")// + .label(16, "First Communication Ok")// + ), new UnsignedWordElement(0x103, inverterStatus = new StatusBitChannel("InverterStatus", this)// .label(1, "Ready to Power on")// .label(2, "Ready for Operating")// .label(4, "Enabled")// @@ -301,22 +304,22 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .label(4096, "DC relays 1 close")// .label(8192, "DC relays 2 close")// .label(16384, "Mains OK")// - ), new UnsignedWordElement(0x104, errorCode = new ModbusReadLongChannel("ErrorCode", this)), + ), new UnsignedWordElement(0x104, errorCode = new ModbusReadLongChannel("ErrorCode", this)), new UnsignedWordElement(0x105, dcDcStatus = new StatusBitChannel("DCDCStatus", this)// - .label(1, "Ready to Power on")// - .label(2, "Ready for Operating")// - .label(4, "Enabled")// - .label(8, "DCDC Fault")// - .label(128, "DCDC Warning")// - .label(256, "Voltage/Current mode")// - .label(512, "Power mode")// - ), new UnsignedWordElement(0x106, dcDcError = new ModbusReadLongChannel("DCDCError", this)), + .label(1, "Ready to Power on")// + .label(2, "Ready for Operating")// + .label(4, "Enabled")// + .label(8, "DCDC Fault")// + .label(128, "DCDC Warning")// + .label(256, "Voltage/Current mode")// + .label(512, "Power mode")// + ), new UnsignedWordElement(0x106, dcDcError = new ModbusReadLongChannel("DCDCError", this)), new SignedWordElement(0x107, batteryCurrentPcs = new ModbusReadLongChannel("BatteryCurrentPcs", this).unit("mA") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x108, batteryVoltagePcs = new ModbusReadLongChannel("BatteryVoltagePcs", this).unit("mV") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x109, current = new ModbusReadLongChannel("Current", this).unit("mA").multiplier(2)), // new SignedWordElement(0x10A, @@ -329,25 +332,25 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { activePower = new ModbusReadLongChannel("ActivePower", this).unit("W").multiplier(2)), // new SignedWordElement(0x10E, activePowerL1 = new ModbusReadLongChannel("ActivePowerL1", this).unit("W") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x10F, activePowerL2 = new ModbusReadLongChannel("ActivePowerL2", this).unit("W") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x110, activePowerL3 = new ModbusReadLongChannel("ActivePowerL3", this).unit("W") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x111, reactivePower = new ModbusReadLongChannel("ReactivePower", this).unit("Var") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x112, reactivePowerL1 = new ModbusReadLongChannel("ReactivePowerL1", this).unit("Var") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x113, reactivePowerL2 = new ModbusReadLongChannel("ReactivePowerL2", this).unit("Var") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x114, reactivePowerL3 = new ModbusReadLongChannel("ReactivePowerL3", this).unit("Var") - .multiplier(2)), // + .multiplier(2)), // new SignedWordElement(0x115, cosPhi3p = new ModbusReadLongChannel("CosPhi3p", this).unit("")), // new SignedWordElement(0x116, cosPhiL1 = new ModbusReadLongChannel("CosPhiL1", this).unit("")), // new SignedWordElement(0x117, cosPhiL2 = new ModbusReadLongChannel("CosPhiL2", this).unit("")), // @@ -355,45 +358,45 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new ModbusInputRegisterRange(0x11A, // new SignedWordElement(0x11A, pcsAllowedCharge = new ModbusReadLongChannel("PcsAllowedCharge", this).unit("kW") - .multiplier(2)), + .multiplier(2)), new SignedWordElement(0x11B, pcsAllowedDischarge = new ModbusReadLongChannel("PcsAllowedDischarge", this).unit("kW") - .multiplier(2)), + .multiplier(2)), new UnsignedWordElement(0x11C, // batteryState = new ModbusReadLongChannel("BatteryState", this)// - .label(0, "Initial")// - .label(1, STOP)// - .label(2, "Starting")// - .label(3, START)// - .label(4, "Stopping")// - .label(5, "Fault")), // + .label(0, "Initial")// + .label(1, STOP)// + .label(2, "Starting")// + .label(3, START)// + .label(4, "Stopping")// + .label(5, "Fault")), // new UnsignedWordElement(0x11D, // batteryMode = new ModbusReadLongChannel("BatteryMode", this).label(0, "Normal Mode")), new SignedWordElement(0x11E, batteryVoltage = new ModbusReadLongChannel("BatteryVoltage", this).unit("mV") - .multiplier(2)), + .multiplier(2)), new SignedWordElement(0x11F, batteryCurrent = new ModbusReadLongChannel("BatteryCurrent", this).unit("mA") - .multiplier(2)), + .multiplier(2)), new SignedWordElement(0x120, // batteryPower = new ModbusReadLongChannel("BatteryPower", this).unit("W")// - .multiplier(2)), + .multiplier(2)), new UnsignedWordElement(0x121, // soc = new ModbusReadLongChannel("Soc", this).unit("%")), new UnsignedWordElement(0x122, // allowedChargeCurrent = new ModbusReadLongChannel("AllowedChargeCurrent", this) - .unit("mA")// - .multiplier(2)// - .negate()), + .unit("mA")// + .multiplier(2)// + .negate()), new UnsignedWordElement(0x123, // allowedDischargeCurrent = new ModbusReadLongChannel("AllowedDischargeCurrent", this) - .unit("mA").multiplier(2)), + .unit("mA").multiplier(2)), new UnsignedWordElement(0x124, // allowedCharge = new ModbusReadLongChannel("AllowedCharge", this).unit("W").multiplier(2) - .negate()), + .negate()), new UnsignedWordElement(0x125, // allowedDischarge = new ModbusReadLongChannel("AllowedDischarge", this).unit("W") - .multiplier(2)), + .multiplier(2)), new SignedDoublewordElement(0x126, // batteryChargeEnergy = new ModbusReadLongChannel("BatteryChargeEnergy", this).unit("kWh")).wordorder(WordOrder.LSWMSW), @@ -402,22 +405,22 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { "BatteryDischargeEnergy", this).unit("kWh")).wordorder(WordOrder.LSWMSW), new UnsignedWordElement(0x12A, // batteryOperationStatus = new StatusBitChannel("BatteryOperationStatus", this) - .label(1, "Battery group 1 operating")// - .label(2, "Battery group 2 operating")// - .label(4, "Battery group 3 operating")// - .label(8, "Battery group 4 operating")), + .label(1, "Battery group 1 operating")// + .label(2, "Battery group 2 operating")// + .label(4, "Battery group 3 operating")// + .label(8, "Battery group 4 operating")), new UnsignedWordElement(0x12B, // batteryHighestVoltage = new ModbusReadLongChannel("BatteryHighestVoltage", this) - .unit("mV")), + .unit("mV")), new UnsignedWordElement(0x12C, // batteryLowestVoltage = new ModbusReadLongChannel("BatteryLowestVoltage", this) - .unit("mV")), + .unit("mV")), new SignedWordElement(0x12D, // batteryHighestTemperature = new ModbusReadLongChannel("BatteryHighestTemperature", this) - .unit("�C")), + .unit("�C")), new SignedWordElement(0x12E, // batteryLowestTemperature = new ModbusReadLongChannel("BatteryLowestTemperature", this) - .unit("�C")), + .unit("�C")), new UnsignedWordElement(0x12F, // batteryStopRequest = new ModbusReadLongChannel("BatteryStopRequest", this)), new UnsignedWordElement(0x130, @@ -513,9 +516,9 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .label(8, "Pre-charge failure on battery control group 4"))), new UnsignedWordElement(0x13D, batteryFault7 = warning.channel(new StatusBitChannel("BatteryFault7", this)// - )), new UnsignedWordElement(0x13E, - batteryFault8 = warning.channel(new StatusBitChannel("BatteryFault8", this)// - )), + )), new UnsignedWordElement(0x13E, + batteryFault8 = warning.channel(new StatusBitChannel("BatteryFault8", this)// + )), new UnsignedWordElement(0x13F, batteryFault9 = warning.channel(new StatusBitChannel("BatteryFault9", this)// .label(4, "Sampling circuit abnormal for BMU")// @@ -542,135 +545,129 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .label(16384, "BMU power contactor open"))), new UnsignedWordElement(0x141, batteryFault11 = warning.channel(new StatusBitChannel("BatteryFault11", this)// - )), new UnsignedWordElement(0x142, - batteryFault12 = warning.channel(new StatusBitChannel("BatteryFault12", this)// - )), + )), new UnsignedWordElement(0x142, + batteryFault12 = warning.channel(new StatusBitChannel("BatteryFault12", this)// + )), new UnsignedWordElement(0x143, batteryFault13 = warning.channel(new StatusBitChannel("BatteryFault13", this)// - )), new UnsignedWordElement(0x144, - batteryFault14 = warning.channel(new StatusBitChannel("BatteryFault14", this)// - )), new UnsignedWordElement(0x145, batteryGroupControlStatus = warning - .channel(new StatusBitChannel("BatteryGroupControlStatus", this)// - )), new UnsignedWordElement(0x146, - errorLog1 = warning.channel(new StatusBitChannel("ErrorLog1", this)// - )), new UnsignedWordElement(0x147, - errorLog2 = warning.channel(new StatusBitChannel("ErrorLog2", this)// - )), new UnsignedWordElement(0x148, - errorLog3 = warning.channel(new StatusBitChannel("ErrorLog3", this)// - )), + )), new UnsignedWordElement(0x144, + batteryFault14 = warning.channel(new StatusBitChannel("BatteryFault14", this)// + )), new UnsignedWordElement(0x145, batteryGroupControlStatus = warning + .channel(new StatusBitChannel("BatteryGroupControlStatus", this)// + )), new UnsignedWordElement(0x146, + errorLog1 = warning.channel(new StatusBitChannel("ErrorLog1", this)// + )), new UnsignedWordElement(0x147, + errorLog2 = warning.channel(new StatusBitChannel("ErrorLog2", this)// + )), new UnsignedWordElement(0x148, + errorLog3 = warning.channel(new StatusBitChannel("ErrorLog3", this)// + )), new UnsignedWordElement(0x149, errorLog4 = warning.channel(new StatusBitChannel("ErrorLog4", this)// - )), new UnsignedWordElement(0x14a, - errorLog5 = warning.channel(new StatusBitChannel("ErrorLog5", this)// - )), new UnsignedWordElement(0x14b, - errorLog6 = warning.channel(new StatusBitChannel("ErrorLog6", this)// - )), + )), new UnsignedWordElement(0x14a, + errorLog5 = warning.channel(new StatusBitChannel("ErrorLog5", this)// + )), new UnsignedWordElement(0x14b, + errorLog6 = warning.channel(new StatusBitChannel("ErrorLog6", this)// + )), new UnsignedWordElement(0x14c, errorLog7 = warning.channel(new StatusBitChannel("ErrorLog7", this)// - )), new UnsignedWordElement(0x14d, - errorLog8 = warning.channel(new StatusBitChannel("ErrorLog8", this)// - )), new UnsignedWordElement(0x14e, - errorLog9 = warning.channel(new StatusBitChannel("ErrorLog9", this)// - )), + )), new UnsignedWordElement(0x14d, + errorLog8 = warning.channel(new StatusBitChannel("ErrorLog8", this)// + )), new UnsignedWordElement(0x14e, + errorLog9 = warning.channel(new StatusBitChannel("ErrorLog9", this)// + )), new UnsignedWordElement(0x14f, errorLog10 = warning.channel(new StatusBitChannel("ErrorLog10", this)// - )), new UnsignedWordElement(0x150, - errorLog11 = warning.channel(new StatusBitChannel("ErrorLog11", this)// - )), new UnsignedWordElement(0x151, - errorLog12 = warning.channel(new StatusBitChannel("ErrorLog12", this)// - )), + )), new UnsignedWordElement(0x150, + errorLog11 = warning.channel(new StatusBitChannel("ErrorLog11", this)// + )), new UnsignedWordElement(0x151, + errorLog12 = warning.channel(new StatusBitChannel("ErrorLog12", this)// + )), new UnsignedWordElement(0x152, errorLog13 = warning.channel(new StatusBitChannel("ErrorLog13", this)// - )), new UnsignedWordElement(0x153, - errorLog14 = warning.channel(new StatusBitChannel("ErrorLog14", this)// - )), new UnsignedWordElement(0x154, - errorLog15 = warning.channel(new StatusBitChannel("ErrorLog15", this)// - )), + )), new UnsignedWordElement(0x153, + errorLog14 = warning.channel(new StatusBitChannel("ErrorLog14", this)// + )), new UnsignedWordElement(0x154, + errorLog15 = warning.channel(new StatusBitChannel("ErrorLog15", this)// + )), new UnsignedWordElement(0x155, errorLog16 = warning.channel(new StatusBitChannel("ErrorLog16", this)// - ))), new WriteableModbusRegisterRange(0x200, // - new UnsignedWordElement(0x200, // - setWorkState = new ModbusWriteLongChannel("SetWorkState", this) // + ))), new WriteableModbusRegisterRange(0x200, // + new UnsignedWordElement(0x200, // + setWorkState = new ModbusWriteLongChannel("SetWorkState", this) // .label(0, STOP) // .label(1, START)), - new UnsignedWordElement(0x201, // - setSystemErrorReset = new ModbusWriteLongChannel("SetSystemErrorReset", - this)// - .label(0, OFF)// - .label(1, ON)), - new UnsignedWordElement(0x202, // - setOperationMode = new ModbusWriteLongChannel("SetOperationMode", this)// + new UnsignedWordElement(0x201, // + setSystemErrorReset = new ModbusWriteLongChannel("SetSystemErrorReset", + this)// + .label(0, OFF)// + .label(1, ON)), + new UnsignedWordElement(0x202, // + setOperationMode = new ModbusWriteLongChannel("SetOperationMode", this)// .label(0, "P/Q Set point")// .label(1, "IAC / cosphi set point"))), new WriteableModbusRegisterRange(0x203, new SignedWordElement(0x203, // setActivePower = new ModbusWriteLongChannel("SetActivePower", this)// - .unit("W").multiplier(2))), + .unit("W").multiplier(2))), new WriteableModbusRegisterRange(0x204, new SignedWordElement(0x204, // setActivePowerL1 = new ModbusWriteLongChannel("SetActivePowerL1", this)// - .unit("W").multiplier(2)), + .unit("W").multiplier(2)), new SignedWordElement(0x205, // setActivePowerL2 = new ModbusWriteLongChannel("SetActivePowerL2", this)// - .unit("W").multiplier(2)), + .unit("W").multiplier(2)), new SignedWordElement(0x206, // setActivePowerL3 = new ModbusWriteLongChannel("SetActivePowerL3", this)// - .unit("W").multiplier(2))), + .unit("W").multiplier(2))), new WriteableModbusRegisterRange(0x207, new SignedWordElement(0x207, // setReactivePower = new ModbusWriteLongChannel("SetReactivePower", this)// - .unit("W").multiplier(2))), + .unit("W").multiplier(2))), new WriteableModbusRegisterRange(0x208, new SignedWordElement(0x208, // setReactivePowerL1 = new ModbusWriteLongChannel("SetReactivePowerL1", this)// - .unit("W").multiplier(2)), + .unit("W").multiplier(2)), new SignedWordElement(0x209, // setReactivePowerL2 = new ModbusWriteLongChannel("SetReactivePowerL2", this)// - .unit("W").multiplier(2)), + .unit("W").multiplier(2)), new SignedWordElement(0x20A, // setReactivePowerL3 = new ModbusWriteLongChannel("SetReactivePowerL3", this)// - .unit("W").multiplier(2)))); - // new ModbusInputRegisterRange(0x6040, - // new UnsignedWordElement(0x6040, // - // batteryInformation1 = new ModbusReadLongChannel("BatteryInformation1", this)), - // new UnsignedWordElement(0x6041, // - // batteryInformation2 = new ModbusReadLongChannel("BatteryInformation2", this)), - // new UnsignedWordElement(0x6042, // - // batteryInformation3 = new ModbusReadLongChannel("BatteryInformation3", this)), - // new UnsignedWordElement(0x6043, // - // batteryInformation4 = new ModbusReadLongChannel("BatteryInformation4", this))), - // new ModbusInputRegisterRange(0x6840, - // new UnsignedWordElement(0x6840, // - // batteryInformation5 = new ModbusReadLongChannel("BatteryInformation5", this)), - // new UnsignedWordElement(0x6841, // - // batteryInformation6 = new ModbusReadLongChannel("BatteryInformation6", this)), - // new UnsignedWordElement(0x6842, // - // batteryInformation7 = new ModbusReadLongChannel("BatteryInformation7", this)), - // new UnsignedWordElement(0x6843, // - // batteryInformation8 = new ModbusReadLongChannel("BatteryInformation8", this))), - // new ModbusInputRegisterRange(0x7640, - // new UnsignedWordElement(0x7640, // - // batteryInformation9 = new ModbusReadLongChannel("BatteryInformation9", this)), - // new UnsignedWordElement(0x7641, // - // batteryInformation10 = new ModbusReadLongChannel("BatteryInformation10", this)), - // new UnsignedWordElement(0x7642, // - // batteryInformation11 = new ModbusReadLongChannel("BatteryInformation11", this)), - // new UnsignedWordElement(0x7643, // - // batteryInformation12 = new ModbusReadLongChannel("BatteryInformation12", this))), - // new ModbusInputRegisterRange(0x8440, - // new UnsignedWordElement(0x8440, // - // batteryInformation13 = new ModbusReadLongChannel("BatteryInformation13", this)), - // new UnsignedWordElement(0x8441, // - // batteryInformation14 = new ModbusReadLongChannel("BatteryInformation14", this)), - // new UnsignedWordElement(0x8442, // - // batteryInformation15 = new ModbusReadLongChannel("BatteryInformation15", this)), - // new UnsignedWordElement(0x8443, // - // batteryInformation16 = new ModbusReadLongChannel("BatteryInformation16", this))), - // new ModbusInputRegisterRange(0x9240, - // new UnsignedWordElement(0x9240, // - // batteryInformation17 = new ModbusReadLongChannel("BatteryInformation17", this)), - // new UnsignedWordElement(0x9241, // - // batteryInformation18 = new ModbusReadLongChannel("BatteryInformation18", this)), - // new UnsignedWordElement(0x9242, // - // batteryInformation19 = new ModbusReadLongChannel("BatteryInformation19", this)), - // new UnsignedWordElement(0x9243, // - // batteryInformation20 = new ModbusReadLongChannel("BatteryInformation20", this)))); + .unit("W").multiplier(2)))); + this.power = new SymmetricPowerImpl(100000, setActivePower, setReactivePower); + this.allowedChargeLimit = new PGreaterEqualLimitation(power); + this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); + this.batFullLimit = new NoPBetweenLimitation(power); + this.power.addStaticLimitation(batFullLimit); + this.allowedCharge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedChargeLimit.setP(allowedCharge.valueOptional().orElse(0L)); + if (allowedCharge.isValuePresent()) { + if (allowedCharge.getValue() > -100) { + batFullLimit.setP(0L, 5000L); + } else { + batFullLimit.setP(null, null); + } + } + } + }); + this.power.addStaticLimitation(this.allowedChargeLimit); + this.allowedDischargeLimit = new PSmallerEqualLimitation(power); + this.allowedDischargeLimit.setP(this.allowedDischarge.valueOptional().orElse(0L)); + this.batEmptyLimit = new NoPBetweenLimitation(power); + this.power.addStaticLimitation(batEmptyLimit); + this.allowedDischarge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedDischargeLimit.setP(allowedDischarge.valueOptional().orElse(0L)); + if (allowedDischarge.isValuePresent()) { + if(allowedDischarge.getValue() < 100) { + batEmptyLimit.setP(-5000L, 0L); + }else { + batEmptyLimit.setP(null, null); + } + } + } + }); + return protocol; } @Override @@ -738,4 +735,9 @@ public WriteChannel setReactivePowerL3() { return setReactivePowerL3; } + @Override + public SymmetricPowerImpl getPower() { + return power; + } + } diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index 6bfc603b6b4..67eed54f971 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -50,6 +50,9 @@ import io.openems.core.ThingRepository; import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; import io.openems.impl.protocol.simulator.SimulatorDeviceNature; import io.openems.impl.protocol.simulator.SimulatorReadChannel; @@ -71,6 +74,9 @@ public class SimulatorSymmetricEss extends SimulatorDeviceNature implements Symm "reactivePowerGeneratorConfig", this).addChangeListener(this).addChangeListener(this); private LoadGenerator offGridActivePowerGenerator; private LoadGenerator offGridReactivePowerGenerator; + private SymmetricPowerImpl power; + private PGreaterEqualLimitation allowedChargeLimit; + private PSmallerEqualLimitation allowedDischargeLimit; /* * Constructors @@ -111,6 +117,26 @@ public SimulatorSymmetricEss(String thingId, Device parent) throws ConfigExcepti } return 0L; }, this.activePower); + power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower); + this.allowedChargeLimit = new PGreaterEqualLimitation(power); + this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); + this.allowedCharge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedChargeLimit.setP(allowedCharge.valueOptional().orElse(0L)); + } + }); + this.power.addStaticLimitation(this.allowedChargeLimit); + this.allowedDischargeLimit = new PSmallerEqualLimitation(power); + this.allowedDischargeLimit.setP(this.allowedDischarge.valueOptional().orElse(0L)); + this.allowedDischarge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedDischargeLimit.setP(allowedDischarge.valueOptional().orElse(0L)); + } + }); } /* @@ -120,10 +146,10 @@ public SimulatorSymmetricEss(String thingId, Device parent) throws ConfigExcepti private ConfigChannel chargeSoc = new ConfigChannel("chargeSoc", this); @ChannelInfo(title = "GridMode", type = Long.class) public ConfigChannel gridMode = new ConfigChannel("gridMode", this).label(0L, ON_GRID) - .label(1L, OFF_GRID).defaultValue(0L); + .label(1L, OFF_GRID).defaultValue(0L); @ChannelInfo(title = "SystemState", type = Long.class) public ConfigChannel systemState = new ConfigChannel("systemState", this) // - .label(1L, START).label(2L, STOP).label(5L, FAULT).defaultValue(1L); + .label(1L, START).label(2L, STOP).label(5L, FAULT).defaultValue(1L); @Override public ConfigChannel minSoc() { @@ -202,16 +228,6 @@ public ReadChannel reactivePower() { return reactivePower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - @Override public StatusBitChannels warning() { return warning; @@ -346,4 +362,9 @@ private void getCharger() { } } + @Override + public SymmetricPowerImpl getPower() { + return power; + } + } diff --git a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java index 65ffca682df..1b651da8603 100644 --- a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java +++ b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java @@ -1,5 +1,9 @@ package io.openems.impl.device.sma; +import java.util.Optional; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; @@ -10,6 +14,9 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; +import io.openems.core.utilities.power.PGreaterEqualLimitation; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; @@ -59,6 +66,9 @@ public ConfigChannel chargeSoc() { private ReadChannel allowedApparent = new StaticValueChannel("AllowedApparent", this, 6000L); private StaticValueChannel nominalPower = new StaticValueChannel("maxNominalPower", this, 6000l) .unit("VA"); + private SymmetricPowerImpl power; + private PGreaterEqualLimitation allowedChargeLimit; + private PSmallerEqualLimitation allowedDischargeLimit; public ModbusReadLongChannel frequency; public ModbusReadLongChannel current; public ModbusReadLongChannel voltage; @@ -138,16 +148,6 @@ public ReadChannel reactivePower() { return reactivePower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - @Override protected ModbusProtocol defineModbusProtocol() throws ConfigException { warning = new StatusBitChannels("Warning", this); @@ -199,7 +199,32 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new UnsignedDoublewordElement(41187, meterSetting = new ModbusWriteLongChannel("MeterSetting", this) .label(3053, "SMA Energy Meter").label(3547, "Wechselrichter")))); + this.power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower); + this.allowedChargeLimit = new PGreaterEqualLimitation(power); + this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); + this.allowedCharge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedChargeLimit.setP(allowedCharge.valueOptional().orElse(0L)); + } + }); + this.power.addStaticLimitation(this.allowedChargeLimit); + this.allowedDischargeLimit = new PSmallerEqualLimitation(power); + this.allowedDischargeLimit.setP(this.allowedDischarge.valueOptional().orElse(0L)); + this.allowedDischarge.addChangeListener(new ChannelChangeListener() { + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + allowedDischargeLimit.setP(allowedDischarge.valueOptional().orElse(0L)); + } + }); return protokol; } + @Override + public SymmetricPowerImpl getPower() { + return power; + } + } diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java index ec62384b11f..593829b01aa 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java @@ -13,6 +13,7 @@ import io.openems.api.device.Device; import io.openems.api.device.nature.ess.EssNature; import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.device.simulator.SimulatorTools; import io.openems.test.utils.channel.UnitTestConfigChannel; import io.openems.test.utils.channel.UnitTestReadChannel; @@ -38,6 +39,7 @@ public class UnitTestSymmetricEssNature implements SymmetricEssNature { public UnitTestWriteChannel setReactivePower = new UnitTestWriteChannel<>("SetReactivePower", this); public StaticValueChannel capacity = new StaticValueChannel("Capacity", this, SimulatorTools.getRandomLong(3000, 50000)); + public SymmetricPowerImpl power = new SymmetricPowerImpl(9000, setActivePower, setReactivePower); private final String id; public UnitTestSymmetricEssNature(String id) { @@ -121,17 +123,7 @@ public ReadChannel reactivePower() { @Override public ReadChannel maxNominalPower() { - return maxNominalPower(); - } - - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; + return maxNominalPower; } @Override @@ -163,4 +155,9 @@ public List getWriteTasks() { return null; } + @Override + public SymmetricPowerImpl getPower() { + return power; + } + } From 536aeda663ae0be56f5478f3606839939be64060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Feb 2018 10:53:27 +0100 Subject: [PATCH 036/156] add ReactivePowerLimitation Controller --- .../ReactivePowerLimitationController.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java new file mode 100644 index 00000000000..e692bca3c80 --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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.symmetric.powerlimitation; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "Power limitation (Symmetric)", description = "Limits the active and reactive power of the Ess. For symmetric Ess.") +public class ReactivePowerLimitationController extends Controller { + + /* + * Constructors + */ + public ReactivePowerLimitationController() { + super(); + } + + public ReactivePowerLimitationController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Min-Charge ActivePower", description = "The minimum allowed active power for discharge. Value is negative.", type = Long.class) + public ConfigChannel pMin = new ConfigChannel("pMin", this); + + @ChannelInfo(title = "Max-Charge ActivePower", description = "The maximum allowed active power for discharge. Value is positive.", type = Long.class) + public ConfigChannel pMax = new ConfigChannel("pMax", this); + + @ChannelInfo(title = "Min-Charge ReactivePower", description = "The minimum allowed reactive power for discharge. Value is negative.", type = Long.class) + public ConfigChannel qMin = new ConfigChannel("qMin", this); + + @ChannelInfo(title = "Max-Charge ReactivePower", description = "The maximum allowed reactive power for discharge. Value is positive.", type = Long.class) + public ConfigChannel qMax = new ConfigChannel("qMax", this); + + /* + * Methods + */ + @Override + public void run() { + try { + Ess ess = this.ess.value(); + if (qMin.isValuePresent()) { + ess.minReactivePowerLimit.setQ(qMin.valueOptional().orElse(null)); + try { + ess.power.applyLimitation(ess.minReactivePowerLimit); + } catch (PowerException e) { + log.error("Failed to write Min Q",e); + } + } + if(qMax.isValuePresent()) { + ess.maxReactivePowerLimit.setQ(qMax.valueOptional().orElse(null)); + try { + ess.power.applyLimitation(ess.maxReactivePowerLimit); + } catch (PowerException e) { + log.error("Failed to write Max Q",e); + } + } + } catch (InvalidValueException e) { + log.error("No ess found.", e); + } + } + +} From eda1cb860d316809f0d34a8fe22132fb74c60666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Feb 2018 10:55:14 +0100 Subject: [PATCH 037/156] move requiredTime calculation to the bride change BridgeEventListener from abstract class to interface --- edge/src/io/openems/api/bridge/Bridge.java | 18 ++++++++++++------ .../api/bridge/BridgeEventListener.java | 16 ++-------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/edge/src/io/openems/api/bridge/Bridge.java b/edge/src/io/openems/api/bridge/Bridge.java index 10b8113108c..5eb2e547e9e 100644 --- a/edge/src/io/openems/api/bridge/Bridge.java +++ b/edge/src/io/openems/api/bridge/Bridge.java @@ -22,8 +22,10 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -53,7 +55,7 @@ public abstract class Bridge extends Thread implements Thing { protected final List devices = Collections.synchronizedList(new LinkedList()); protected final Logger log; private DebugChannel requiredCycleTime = new DebugChannel<>("RequiredCycleTime", this); - private List eventListener = new ArrayList<>(); + private Map eventListener = new HashMap<>(); private DebugChannel readOtherTaskReadCount = new DebugChannel<>("ReadOtherTaskReadCount", this); /** @@ -72,7 +74,7 @@ public String id() { } public void addListener(BridgeEventListener listener) { - this.eventListener.add(listener); + this.eventListener.put(listener, 0L); } public void removeListener(BridgeEventListener listener) { @@ -341,15 +343,19 @@ public final void triggerInitialize() { private void notifyListeners(Position position) { BridgeEvent event = new BridgeEvent(position); - for (BridgeEventListener listener : this.eventListener) { - listener.executeNotify(event); + for (BridgeEventListener listener : this.eventListener.keySet()) { + long timeBeforeExecute = System.currentTimeMillis(); + listener.onBridgeChange(event); + this.eventListener.put(listener, System.currentTimeMillis() - timeBeforeExecute); } } private long requiredTimeListeners() { long time = 0; - for (BridgeEventListener listener : this.eventListener) { - time = listener.getRequiredTime(); + for (Long timeListener : this.eventListener.values()) { + if (timeListener != null) { + time += timeListener; + } } return time; } diff --git a/edge/src/io/openems/api/bridge/BridgeEventListener.java b/edge/src/io/openems/api/bridge/BridgeEventListener.java index 366d5b66696..c7252a5833d 100644 --- a/edge/src/io/openems/api/bridge/BridgeEventListener.java +++ b/edge/src/io/openems/api/bridge/BridgeEventListener.java @@ -1,19 +1,7 @@ package io.openems.api.bridge; -public abstract class BridgeEventListener { +public interface BridgeEventListener { - private long requiredTime = 0; - - public void executeNotify(BridgeEvent event) { - long beforeExecute = System.currentTimeMillis(); - notify(event); - requiredTime = System.currentTimeMillis() - beforeExecute; - } - - public long getRequiredTime() { - return requiredTime; - } - - protected abstract void notify(BridgeEvent event); + void onBridgeChange(BridgeEvent event); } From fe459c5aa840d9ce48373357981463949236d05c Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 09:41:47 +0100 Subject: [PATCH 038/156] Static Utils class for EdgeWebsocketServer --- .../impl/provider/EdgeWebsocketServer.java | 19 +------------ .../edgewebsocket/impl/provider/Utils.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/Utils.java diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 62b3b291749..137031dcc7a 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -3,14 +3,11 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.java_websocket.WebSocket; import org.java_websocket.framing.CloseFrame; import org.java_websocket.handshake.ClientHandshake; import org.osgi.service.event.Event; -import org.osgi.service.event.EventConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +36,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { String apikey = ""; try { // get apikey from handshake - Optional apikeyOpt = parseApikeyFromHandshake(handshake); + Optional apikeyOpt = Utils.parseApikeyFromHandshake(handshake); if (!apikeyOpt.isPresent()) { throw new OpenemsException("Apikey is missing in handshake"); } @@ -123,18 +120,4 @@ protected void _onError(WebSocket websocket, Exception ex) { protected void _onClose(WebSocket websocket) { System.out.println("_onClose"); } - - /** - * Parses the apikey from websocket onOpen handshake - * - * @param handshake - * @return - */ - private Optional parseApikeyFromHandshake(ClientHandshake handshake) { - if (handshake.hasFieldValue("apikey")) { - String apikey = handshake.getFieldValue("apikey"); - return Optional.ofNullable(apikey); - } - return Optional.empty(); - } } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/Utils.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/Utils.java new file mode 100644 index 00000000000..513323a1a2e --- /dev/null +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/Utils.java @@ -0,0 +1,27 @@ +package io.openems.backend.edgewebsocket.impl.provider; + +import java.util.Optional; + +import org.java_websocket.handshake.ClientHandshake; + +public class Utils { + + private Utils() { + + } + + + /** + * Parses the apikey from websocket onOpen handshake + * + * @param handshake + * @return + */ + protected static Optional parseApikeyFromHandshake(ClientHandshake handshake) { + if (handshake.hasFieldValue("apikey")) { + String apikey = handshake.getFieldValue("apikey"); + return Optional.ofNullable(apikey); + } + return Optional.empty(); + } +} From ec093dd345f2815dd87e0f266bcc1372730c2d53 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 09:41:58 +0100 Subject: [PATCH 039/156] Handle EdgeOnline-events --- io.openems.backend.application/bnd.bnd | 3 ++- .../backend/application/EdgeEventHandler.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index cc2e7a0b852..554bd4f7116 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -17,7 +17,8 @@ Private-Package: \ io.openems.backend.timedata.api;version=latest,\ io.openems.backend.edgewebsocket.api;version=latest,\ io.openems.backend.uiwebsocket.api;version=latest,\ - io.openems.wrapper.websocket;version=latest + io.openems.wrapper.websocket;version=latest,\ + io.openems.backend.common;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java b/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java new file mode 100644 index 00000000000..08111134bec --- /dev/null +++ b/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java @@ -0,0 +1,18 @@ +package io.openems.backend.application; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; + +import io.openems.backend.common.events.BackendEventConstants; + +@Component(property = EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_ONLINE) +public class EdgeEventHandler implements EventHandler { + + @Override + public void handleEvent(Event event) { + System.out.println(event); + } + +} From 9b2259414c9627abdafa73f09bc27d5a6d23aa85 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 09:56:15 +0100 Subject: [PATCH 040/156] Handle Edge disconnect --- .../backend/application/EdgeEventHandler.java | 6 ++-- .../common/events/BackendEventConstants.java | 2 ++ .../impl/provider/EdgeWebsocketServer.java | 31 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java b/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java index 08111134bec..03750cc92b1 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java +++ b/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java @@ -7,12 +7,14 @@ import io.openems.backend.common.events.BackendEventConstants; -@Component(property = EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_ONLINE) +@Component(property = { // + EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_ONLINE, + EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_OFFLINE }) public class EdgeEventHandler implements EventHandler { @Override public void handleEvent(Event event) { System.out.println(event); } - + } diff --git a/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java b/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java index b1746afe2d6..c6ca183253a 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java +++ b/io.openems.backend.common/src/io/openems/backend/common/events/BackendEventConstants.java @@ -12,6 +12,8 @@ private BackendEventConstants() { public static final String TOPIC_EDGE_ONLINE = TOPIC_EDGE + "ONLINE"; + public static final String TOPIC_EDGE_OFFLINE = TOPIC_EDGE + "OFFLINE"; + public static final String PROPERTY_KEY_EDGE_ID = "edgeId"; } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 137031dcc7a..a8dc719bcf5 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -58,6 +58,9 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { } } + // store edgeIds together with WebSocket + websocket.setAttachment(edgeIds); + // send successful reply to openems JsonObject jReply = DefaultMessages.openemsConnectionSuccessfulReply(); WebSocketUtils.send(websocket, jReply); @@ -119,5 +122,33 @@ protected void _onError(WebSocket websocket, Exception ex) { @Override protected void _onClose(WebSocket websocket) { System.out.println("_onClose"); + + // get edgeIds from websocket + int[] edgeIds = websocket.getAttachment(); + + // remove websocket from local map + for (int edgeId : edgeIds) { + synchronized (this.websocketsMap) { + this.websocketsMap.remove(edgeId, websocket); + } + } + + // announce device as offline + for (int edgeId : edgeIds) { + Map properties = new HashMap<>(); + properties.put(BackendEventConstants.PROPERTY_KEY_EDGE_ID, edgeId); + Event event = new Event(BackendEventConstants.TOPIC_EDGE_OFFLINE, properties); + this.parent.eventAdmin.postEvent(event); + } + + // log + for (int edgeId : edgeIds) { + Optional deviceOpt = this.parent.metadataService.getDevice(edgeId); + if (deviceOpt.isPresent()) { + log.info("Device [" + deviceOpt.get() + "] disconnected."); + } else { + log.info("Device [ID:" + edgeId + "] disconnected."); + } + } } } From 90ac7a6adab0052ce6c02f5e99b65c99881e60c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:36:04 +0100 Subject: [PATCH 041/156] remove average calculation for ess and meter --- .../symmetric/balancing/BalancingController.java | 8 ++------ .../BalancingBandgapActivePowerController.java | 8 +------- .../BalancingBandgapReactivePowerController.java | 8 +------- .../balancingsurplus/BalancingSurplusController.java | 8 +------- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java b/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java index 857fe927808..031be1b38e3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java @@ -31,7 +31,6 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Self-consumption optimization (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. Ess-Cluster is supported.") @@ -57,7 +56,6 @@ public BalancingController(String thingId) { @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) public final ConfigChannel meter = new ConfigChannel("meter", this); - private AvgFiFoQueue meterPower = new AvgFiFoQueue(2, 1.5); /* * Methods @@ -70,14 +68,12 @@ public void run() { List useableEss = getUseableEss(); if (useableEss.size() > 0) { // Calculate required sum values - meterPower.add(meter.value().activePower.value()); - long calculatedPower = meterPower.avg(); + long calculatedPower = meter.value().activePower.value(); long maxChargePower = 0; long maxDischargePower = 0; long useableSoc = 0; for (Ess ess : useableEss) { - ess.powerAvg.add(ess.activePower.value()); - calculatedPower += ess.powerAvg.avg(); + calculatedPower += ess.activePower.value(); maxChargePower += ess.power.getMinP().orElse(0L); maxDischargePower += ess.power.getMaxP().orElse(0L); useableSoc += ess.useableSoc(); diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java index e281fadf3d6..d7fec9165d1 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java @@ -25,7 +25,6 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") @@ -57,9 +56,6 @@ public BalancingBandgapActivePowerController(String thingId) { @ChannelInfo(title = "Max-ActivePower", description = "High boundary of active power bandgap.", type = Integer.class) public final ConfigChannel maxActivePower = new ConfigChannel<>("maxActivePower", this); - private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(2, 1.5); - private AvgFiFoQueue essActivePower = new AvgFiFoQueue(2, 1.5); - /* * Methods */ @@ -68,10 +64,8 @@ public void run() { try { Ess ess = this.ess.value(); Meter meter = this.meter.value(); - meterActivePower.add(meter.activePower.value()); - essActivePower.add(ess.activePower.value()); // Calculate required sum values - long calculatedPower = meterActivePower.avg() + essActivePower.avg(); + long calculatedPower = meter.activePower.value() + ess.activePower.value(); if (calculatedPower >= maxActivePower.value()) { calculatedPower -= maxActivePower.value(); } else if (calculatedPower <= minActivePower.value()) { diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java index f9f44fe06e0..6f7f567e724 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java @@ -27,7 +27,6 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") @@ -59,9 +58,6 @@ public BalancingBandgapReactivePowerController(String thingId) { @ChannelInfo(title = "Max-ReactivePower", description = "High boundary of reactive power bandgap.", type = Integer.class) public final ConfigChannel maxReactivePower = new ConfigChannel<>("maxReactivePower", this); - private AvgFiFoQueue meterReactivePower = new AvgFiFoQueue(2, 1.5); - private AvgFiFoQueue essReactivePower = new AvgFiFoQueue(2, 1.5); - /* * Methods */ @@ -70,10 +66,8 @@ public void run() { try { Ess ess = this.ess.value(); Meter meter = this.meter.value(); - meterReactivePower.add(meter.reactivePower.value()); - essReactivePower.add(ess.reactivePower.value()); // Calculate required sum values - long calculatedReactivePower = meterReactivePower.avg() + essReactivePower.avg(); + long calculatedReactivePower = meter.reactivePower.value() + ess.reactivePower.value(); if (calculatedReactivePower >= maxReactivePower.value()) { calculatedReactivePower -= maxReactivePower.value(); } else if (calculatedReactivePower <= minReactivePower.value()) { diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java index 95ae226accb..bc8d2a9cb82 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java @@ -27,7 +27,6 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.power.PowerException; @ThingInfo(title = "Self-consumption optimization with surplus feed-in (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. If ess is over the surplusMinSoc, the ess discharges with the power of the chargers. ") @@ -59,9 +58,6 @@ public BalancingSurplusController(String thingId) { @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) public final ConfigChannel meter = new ConfigChannel("meter", this); - private AvgFiFoQueue meterActivePower = new AvgFiFoQueue(3, 1.5); - private AvgFiFoQueue essActivePower = new AvgFiFoQueue(3, 1.5); - private long surplus = 0L; private boolean surplusOn = false; @@ -73,10 +69,8 @@ public BalancingSurplusController(String thingId) { public void run() { try { Ess ess = this.ess.value(); - meterActivePower.add(meter.value().activePower.value()); - essActivePower.add((ess.activePower.value() - surplus)); // Calculate required sum values - long calculatedPower = meterActivePower.avg() + essActivePower.avg(); + long calculatedPower = meter.value().activePower.value() + (ess.activePower.value() - surplus); surplus = getSurplusPower(); // in case the storage has surplus it isn't allowed to charge the storage ac if (calculatedPower < 0 && surplus > 0) { From e51b3eb0845eeb7b6894415ca09e885a9f9ac375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:36:43 +0100 Subject: [PATCH 042/156] implements SymmetricPower --- .../impl/device/byd/Bem125ktla01Ess.java | 137 +++++++++--------- 1 file changed, 68 insertions(+), 69 deletions(-) diff --git a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java index 5d37bf711b4..782bb8d4915 100644 --- a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java +++ b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java @@ -30,6 +30,7 @@ import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; +import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadChannel; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; @@ -84,6 +85,7 @@ public ConfigChannel chargeSoc() { private ModbusWriteChannel setWorkState; private StaticValueChannel maxNominalPower = new StaticValueChannel<>("maxNominalPower", this, 0L); private StaticValueChannel capacity = new StaticValueChannel<>("capacity", this, 170000L).unit("Wh"); + private SymmetricPowerImpl power; public StatusBitChannels warning; public ModbusReadChannel sysAlarmInfo; @@ -163,102 +165,99 @@ public ReadChannel maxNominalPower() { return maxNominalPower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - /* * Methods */ @Override protected ModbusProtocol defineModbusProtocol() throws ConfigException { warning = new StatusBitChannels("Warning", this); - return new ModbusProtocol( // + ModbusProtocol protocol = new ModbusProtocol( // new ModbusRegisterRange(0x0100, // new UnsignedWordElement(0x100, // sysAlarmInfo = new ModbusReadLongChannel("SysAlarmInfo", this)// - .label(0, "Warning State")// - .label(1, "Protection State")// - .label(2, "Derating State")// - .label(4, "Charge Forbidden").label(16, "Discharge Forbidden")), + .label(0, "Warning State")// + .label(1, "Protection State")// + .label(2, "Derating State")// + .label(4, "Charge Forbidden").label(16, "Discharge Forbidden")), new UnsignedWordElement(0x101, // sysWorkStatus = new StatusBitChannel("SysWorkStatus", this)// - .label(0, "Initial") // - .label(1, "Fault") // - .label(2, "Stop") // - .label(4, "Hot Standby") // - .label(8, "Monitoring") // - .label(16, "Standby") // - .label(32, "Operation") // - .label(64, "Debug")), // + .label(0, "Initial") // + .label(1, "Fault") // + .label(2, "Stop") // + .label(4, "Hot Standby") // + .label(8, "Monitoring") // + .label(16, "Standby") // + .label(32, "Operation") // + .label(64, "Debug")), // new UnsignedWordElement(0x102, // sysControlMode = new StatusBitChannel("SysControlMode", this)// - .label(0, "Remote") // - .label(1, "Local")), // + .label(0, "Remote") // + .label(1, "Local")), // new DummyElement(0x103)), new ModbusRegisterRange(0x0110, // new UnsignedWordElement(0x110, // sysAlarmInfo = new StatusBitChannel("SysAlarmInfo", this)// - .label(0, "Status abnormal of AC surge protector") // - .label(1, "Close of control switch") // - .label(2, "Emergency stop") // - .label(4, "Status abnormal of frog detector") // - .label(8, "Serious leakage") // - .label(16, "Normal_leakage")), // + .label(0, "Status abnormal of AC surge protector") // + .label(1, "Close of control switch") // + .label(2, "Emergency stop") // + .label(4, "Status abnormal of frog detector") // + .label(8, "Serious leakage") // + .label(16, "Normal_leakage")), // new UnsignedWordElement(0x111, // sysAlarmInfo2 = new StatusBitChannel("SysAlarmInfo2", this)// - .label(0, "Failure of temperature sensor in control cabinet") // - .label(1, "Close of control switch") // - /* - * TODO new OnOffBitItem(9, "Failure_of_humidity_sensor_in_control_cabinet"), // - * new OnOffBitItem(12, "Failure_of_storage_device"), // - * new OnOffBitItem(13, "Exceeding_of_humidity_in_control_cabinet")))); - */ - )), new ModbusRegisterRange(0x1300, new UnsignedWordElement(0x1300, // - batteryStackVoltage = new ModbusReadLongChannel("BatteryStackVoltage", this) + .label(0, "Failure of temperature sensor in control cabinet") // + .label(1, "Close of control switch") // + /* + * TODO new OnOffBitItem(9, "Failure_of_humidity_sensor_in_control_cabinet"), // + * new OnOffBitItem(12, "Failure_of_storage_device"), // + * new OnOffBitItem(13, "Exceeding_of_humidity_in_control_cabinet")))); + */ + )), new ModbusRegisterRange(0x1300, new UnsignedWordElement(0x1300, // + batteryStackVoltage = new ModbusReadLongChannel("BatteryStackVoltage", this) .multiplier(2).unit("mV")), - new UnsignedWordElement(0x1301, // - batteryStackCurrent = new ModbusReadLongChannel("BatteryStackCurrent", this) + new UnsignedWordElement(0x1301, // + batteryStackCurrent = new ModbusReadLongChannel("BatteryStackCurrent", this) .multiplier(2).unit("mA")), - new UnsignedWordElement(0x1302, // - batteryStackPower = new ModbusReadLongChannel("BatteryStackPower", this) + new UnsignedWordElement(0x1302, // + batteryStackPower = new ModbusReadLongChannel("BatteryStackPower", this) .multiplier(2).unit("W")), - new UnsignedWordElement(0x1303, // - batteryStackSoc = soc = new ModbusReadLongChannel("BatteryStackSoc", this) + new UnsignedWordElement(0x1303, // + batteryStackSoc = soc = new ModbusReadLongChannel("BatteryStackSoc", this) .unit("%")), - new UnsignedWordElement(0x1304, // - batteryStackSoh = new ModbusReadLongChannel("BatteryStackSoh", this).unit("%")), - new UnsignedWordElement(0x1305, // - batteryStackMaxChargeCurrent = new ModbusReadLongChannel( - "BatteryStackMaxChargeCurrent", this).multiplier(2).unit("mA")), - new UnsignedWordElement(0x1306, // - batteryStackMaxDischargeCurrent = new ModbusReadLongChannel( - "BatteryStackMaxDischargeCurrent", this).multiplier(2).unit("mA")), - new UnsignedWordElement(0x1307, // - batteryStackMaxChargePower = new ModbusReadLongChannel( - "BatteryStackMaxChargePower", this).multiplier(2).unit("W")), - new UnsignedWordElement(0x1308, // - batteryStackMaxDischargePower = new ModbusReadLongChannel( - "BatteryStackMaxDischargePower", this).multiplier(2).unit("W")), - new UnsignedWordElement(0x1309, // - batteryStackTotalCapacity = new ModbusReadLongChannel( - "BatteryStackTotalCapacity", this).unit("Wh")), - new UnsignedDoublewordElement(0x130A, // - batteryStackTotalCharge = new ModbusReadLongChannel("BatteryStackTotalCharge", - this).unit("kWh")), - new UnsignedDoublewordElement(0x130C, // - batteryStackTotalDischarge = new ModbusReadLongChannel( - "BatteryStackTotalDischarge", this).unit("kWh")))); + new UnsignedWordElement(0x1304, // + batteryStackSoh = new ModbusReadLongChannel("BatteryStackSoh", this).unit("%")), + new UnsignedWordElement(0x1305, // + batteryStackMaxChargeCurrent = new ModbusReadLongChannel( + "BatteryStackMaxChargeCurrent", this).multiplier(2).unit("mA")), + new UnsignedWordElement(0x1306, // + batteryStackMaxDischargeCurrent = new ModbusReadLongChannel( + "BatteryStackMaxDischargeCurrent", this).multiplier(2).unit("mA")), + new UnsignedWordElement(0x1307, // + batteryStackMaxChargePower = new ModbusReadLongChannel( + "BatteryStackMaxChargePower", this).multiplier(2).unit("W")), + new UnsignedWordElement(0x1308, // + batteryStackMaxDischargePower = new ModbusReadLongChannel( + "BatteryStackMaxDischargePower", this).multiplier(2).unit("W")), + new UnsignedWordElement(0x1309, // + batteryStackTotalCapacity = new ModbusReadLongChannel( + "BatteryStackTotalCapacity", this).unit("Wh")), + new UnsignedDoublewordElement(0x130A, // + batteryStackTotalCharge = new ModbusReadLongChannel("BatteryStackTotalCharge", + this).unit("kWh")), + new UnsignedDoublewordElement(0x130C, // + batteryStackTotalDischarge = new ModbusReadLongChannel( + "BatteryStackTotalDischarge", this).unit("kWh")))); + this.power = new SymmetricPowerImpl(125000, setActivePower, setReactivePower); + return protocol; } @Override public StaticValueChannel capacity() { return capacity; } + + @Override + public SymmetricPowerImpl getPower() { + return power; + } } From c421de0d9f748431b587969987c462a78662783d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:37:27 +0100 Subject: [PATCH 043/156] remove Errors TODO implement symmetricPower appropriate --- ...AsymmetricSymmetricCombinationEssNature.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java index 41271b0cfc2..82ca9051fd9 100644 --- a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java +++ b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java @@ -32,6 +32,7 @@ import io.openems.core.Config; import io.openems.core.ThingRepository; import io.openems.core.utilities.ControllerUtils; +import io.openems.core.utilities.power.SymmetricPower; import io.openems.impl.protocol.system.SystemDeviceNature; @ThingInfo(title = "Ess Asymmetric-Symmetric-Combination") @@ -991,16 +992,6 @@ public ReadChannel reactivePower() { return reactivePower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - @Override public void addListener(ThingChannelsUpdatedListener listener) { this.listeners.add(listener); @@ -1482,4 +1473,10 @@ public void onBridgeInitialized() { loadEss(); } + @Override + public SymmetricPower getPower() { + // TODO Auto-generated method stub + return null; + } + } From af50d4f0e35c3d60a1e9d868c2285a69788af6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:37:46 +0100 Subject: [PATCH 044/156] fix event call --- edge/src/io/openems/api/bridge/Bridge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edge/src/io/openems/api/bridge/Bridge.java b/edge/src/io/openems/api/bridge/Bridge.java index 5eb2e547e9e..c8e845ff813 100644 --- a/edge/src/io/openems/api/bridge/Bridge.java +++ b/edge/src/io/openems/api/bridge/Bridge.java @@ -222,11 +222,11 @@ public final void run() { } long timeUntilWrite = scheduler.getCycleStartTime() + scheduler.getRequiredTime() + 10 - requiredTimeListeners(); + notifyListeners(Position.BEFOREREADOTHER1); if (readTasks.size() > 0) { // calculate time until write // run tasks for not required channels if (timeUntilWrite - System.currentTimeMillis() > 0) { - notifyListeners(Position.BEFOREREADOTHER1); readOther(readTasks, timeUntilWrite, false); } } From 8c41fbd3abf690e452c4df2b5374003db433c0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:38:27 +0100 Subject: [PATCH 045/156] generalize methods and add Event after Controller execution --- .../AfterControllerExecutedListener.java | 6 ++++++ .../io/openems/api/scheduler/Scheduler.java | 19 +++++++++++++++++++ .../impl/scheduler/SimpleScheduler.java | 8 -------- .../ChannelThresholdScheduler.java | 8 -------- .../scheduler/time/WeekTimeScheduler.java | 8 -------- 5 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 edge/src/io/openems/api/scheduler/AfterControllerExecutedListener.java diff --git a/edge/src/io/openems/api/scheduler/AfterControllerExecutedListener.java b/edge/src/io/openems/api/scheduler/AfterControllerExecutedListener.java new file mode 100644 index 00000000000..bbd90ee5519 --- /dev/null +++ b/edge/src/io/openems/api/scheduler/AfterControllerExecutedListener.java @@ -0,0 +1,6 @@ +package io.openems.api.scheduler; + +public interface AfterControllerExecutedListener { + + void afterControllerExecuted(); +} diff --git a/edge/src/io/openems/api/scheduler/Scheduler.java b/edge/src/io/openems/api/scheduler/Scheduler.java index 8dbfb806a1c..06854b9a790 100644 --- a/edge/src/io/openems/api/scheduler/Scheduler.java +++ b/edge/src/io/openems/api/scheduler/Scheduler.java @@ -29,6 +29,7 @@ import io.openems.api.bridge.Bridge; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.DebugChannel; +import io.openems.api.channel.WriteChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.exception.ConfigException; @@ -47,6 +48,7 @@ public abstract class Scheduler extends AbstractWorker implements Thing { protected final ThingRepository thingRepository; private Integer actualCycleTime = null; private DebugChannel requiredCycleTime = new DebugChannel<>("RequiredCycleTime", this); + private List listener = new ArrayList<>(); /* * Config @@ -81,10 +83,27 @@ public synchronized List getControllers() { return Collections.unmodifiableList(new ArrayList<>(this.controllers.values())); } + public void addListener(AfterControllerExecutedListener listener) { + this.listener.add(listener); + } + + public void removeListener(AfterControllerExecutedListener listener) { + this.listener.remove(listener); + } + @Override protected void forever() { cycleStartTime = System.currentTimeMillis(); execute(); + for(AfterControllerExecutedListener listener: this.listener) { + listener.afterControllerExecuted(); + } + for (WriteChannel channel : thingRepository.getWriteChannels()) { + channel.shadowCopyAndReset(); + } + for (Bridge bridge : thingRepository.getBridges()) { + bridge.triggerWrite(); + } requiredTime = System.currentTimeMillis() - cycleStartTime; long maxTime = 0; for (Bridge bridge : thingRepository.getBridges()) { diff --git a/edge/src/io/openems/impl/scheduler/SimpleScheduler.java b/edge/src/io/openems/impl/scheduler/SimpleScheduler.java index c7d7c333bd0..c1175b11ee2 100644 --- a/edge/src/io/openems/impl/scheduler/SimpleScheduler.java +++ b/edge/src/io/openems/impl/scheduler/SimpleScheduler.java @@ -25,8 +25,6 @@ import java.util.List; import info.faljse.SDNotify.SDNotify; -import io.openems.api.bridge.Bridge; -import io.openems.api.channel.WriteChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ThingInfo; import io.openems.api.scheduler.Scheduler; @@ -58,12 +56,6 @@ protected void execute() { // TODO: check if WritableChannels can still be changed, before executing controller.executeRun(); } - for (WriteChannel channel : thingRepository.getWriteChannels()) { - channel.shadowCopyAndReset(); - } - for (Bridge bridge : thingRepository.getBridges()) { - bridge.triggerWrite(); - } } @Override diff --git a/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java b/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java index 9c882c093f2..3216cd0a6b2 100644 --- a/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java +++ b/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java @@ -30,11 +30,9 @@ import com.google.gson.JsonObject; import info.faljse.SDNotify.SDNotify; -import io.openems.api.bridge.Bridge; import io.openems.api.channel.Channel; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -133,12 +131,6 @@ protected void execute() { for (Controller controller : controllers) { controller.executeRun(); } - for (WriteChannel channel : thingRepository.getWriteChannels()) { - channel.shadowCopyAndReset(); - } - for (Bridge bridge : thingRepository.getBridges()) { - bridge.triggerWrite(); - } } private List getActiveControllers() { diff --git a/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java b/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java index 1dfa15bfb56..986d3175c09 100644 --- a/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java +++ b/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java @@ -34,9 +34,7 @@ import com.google.gson.JsonObject; import info.faljse.SDNotify.SDNotify; -import io.openems.api.bridge.Bridge; import io.openems.api.channel.ConfigChannel; -import io.openems.api.channel.WriteChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -123,12 +121,6 @@ protected void execute() { controller.executeRun(); } } - for (WriteChannel channel : thingRepository.getWriteChannels()) { - channel.shadowCopyAndReset(); - } - for (Bridge bridge : thingRepository.getBridges()) { - bridge.triggerWrite(); - } } catch (InvalidValueException | DateTimeParseException | ConfigException | ReflectionException e) { log.error(e.getMessage()); } From 3b16238058b128c6feac8a762db22a63a42b44a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:39:35 +0100 Subject: [PATCH 046/156] fix nullpointers --- .../core/utilities/power/SymmetricPower.java | 4 +- .../power/SymmetricPowerClusterImpl.java | 87 +++++++++++++------ 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPower.java b/edge/src/io/openems/core/utilities/power/SymmetricPower.java index 493a48dffae..4da13483941 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPower.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPower.java @@ -182,8 +182,8 @@ protected Point reduceToZero() { Point result = pZero; for (GeometryLocation location : locations) { Geometry g = location.getGeometryComponent(); - if (!g.equals(pZero) && g instanceof Point) { - result = (Point) location.getGeometryComponent(); + if (!g.equals(pZero)) { + result = FACTORY.createPoint(location.getCoordinate()); break; } } diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java index 6e51e76db93..6a88b782a28 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java @@ -12,8 +12,15 @@ import com.vividsolutions.jts.geom.util.AffineTransformation; import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.api.exception.ConfigException; +import io.openems.api.scheduler.AfterControllerExecutedListener; +import io.openems.api.scheduler.Scheduler; +import io.openems.core.Config; +import io.openems.core.SchedulerInitializedEventListener; +import io.openems.core.ThingRepository; -public class SymmetricPowerClusterImpl extends SymmetricPower implements PowerChangeListener { +public class SymmetricPowerClusterImpl extends SymmetricPower +implements PowerChangeListener, AfterControllerExecutedListener { private List dynamicLimitations; private List ess; @@ -21,6 +28,19 @@ public class SymmetricPowerClusterImpl extends SymmetricPower implements PowerCh public SymmetricPowerClusterImpl() { this.dynamicLimitations = new ArrayList<>(); this.ess = new ArrayList<>(); + try { + Config.getInstance().addSchedulerInitializedEventListener(new SchedulerInitializedEventListener() { + + @Override + public void onSchedulerInitialized() { + Scheduler scheduler = ThingRepository.getInstance().getSchedulers().iterator().next(); + scheduler.addListener(SymmetricPowerClusterImpl.this); + } + }); + } catch (ConfigException e) { + log.error("Can't load config"); + } + reset(); } public void addEss(SymmetricEssNature ess) { @@ -33,7 +53,7 @@ public void addEss(SymmetricEssNature ess) { public void removeEss(SymmetricEssNature ess) { this.ess.remove(ess); mergePower(); - ess.getPower().addListener(this); + ess.getPower().removeListener(this); setMaxApparentPower(getMaxApparentPower() - ess.maxNominalPower().valueOptional().orElse(0L)); } @@ -43,26 +63,24 @@ public void powerChanged(Geometry allowedPower) { } private void mergePower() { - Geometry base = null; + Geometry base = FACTORY.createPoint(new Coordinate(0, 0)); for (SymmetricEssNature ess : this.ess) { - if (base != null) { - base = getUnionAround(base, ess.getPower().getGeometry()); - } else { - base = ess.getPower().getGeometry(); - } + base = getUnionAround(base, ess.getPower().getGeometry()); } - for (Limitation limit : this.dynamicLimitations) { - Geometry limitedPower; - try { - limitedPower = limit.applyLimit(base); - if (!limitedPower.isEmpty()) { - base = limitedPower; + synchronized (this.dynamicLimitations) { + for (Limitation limit : this.dynamicLimitations) { + Geometry limitedPower; + try { + limitedPower = limit.applyLimit(base); + if (!limitedPower.isEmpty()) { + base = limitedPower; + } + } catch (PowerException e) { + log.error("Failed to apply Limit after base Power changed!", e); } - } catch (PowerException e) { - log.error("Failed to apply Limit after base Power changed!", e); } + setGeometry(base); } - setGeometry(base); } private Geometry getUnionAround(Geometry g1, Geometry g2) { @@ -84,17 +102,20 @@ private Geometry getUnionAround(Geometry g1, Geometry g2) { @Override public void applyLimitation(Limitation limit) throws PowerException { - Geometry limitedPower = limit.applyLimit(getGeometry()); - if (!limitedPower.isEmpty()) { - setGeometry(limitedPower); - this.dynamicLimitations.add(limit); - } else { - throw new PowerException("No possible Power after applying Limit. Limit is not applied!"); + synchronized (this.dynamicLimitations) { + Geometry limitedPower = limit.applyLimit(getGeometry()); + if (!limitedPower.isEmpty()) { + setGeometry(limitedPower); + this.dynamicLimitations.add(limit); + } else { + throw new PowerException("No possible Power after applying Limit. Limit is not applied!"); + } } } private void setPower() { Point p = reduceToZero(); + setGeometry(p); long activePower = (long) p.getCoordinate().x; long reactivePower = (long) p.getCoordinate().y; long socSum = 0; @@ -217,8 +238,8 @@ private void setPower() { * weight the range of possible power by the useableSoc * if the useableSoc is negative the ess will be charged */ - long power = (long) (Math.ceil(minQ + diff / getMaxApparentPower() - * ess.maxNominalPower().valueOptional().orElse(0L))); + long power = (long) (Math + .ceil(minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L))); QEqualLimitation limit = new QEqualLimitation(ess.getPower()); limit.setQ(power); try { @@ -246,8 +267,8 @@ private void setPower() { } double diff = maxQ - minQ; // weight the range of possible power by the useableSoc - long power = (long) Math.floor(minQ + diff / getMaxApparentPower() - * ess.maxNominalPower().valueOptional().orElse(0L)); + long power = (long) Math + .floor(minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L)); QEqualLimitation limit = new QEqualLimitation(ess.getPower()); limit.setQ(power); try { @@ -260,4 +281,16 @@ private void setPower() { } } + @Override + protected void reset() { + this.dynamicLimitations.clear(); + super.reset(); + } + + @Override + public void afterControllerExecuted() { + setPower(); + reset(); + } + } From 045d9215aab8e55cff9d69c62ef4a2ed805d9620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:39:56 +0100 Subject: [PATCH 047/156] add average calculation for write power --- .../core/utilities/power/SymmetricPowerImpl.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java index ed28881e746..e1970207638 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java @@ -10,7 +10,8 @@ import io.openems.api.bridge.BridgeEvent; import io.openems.api.bridge.BridgeEventListener; import io.openems.api.channel.WriteChannel; -import io.openems.api.exception.WriteChannelException;; +import io.openems.api.exception.WriteChannelException; +import io.openems.core.utilities.AvgFiFoQueue;; public class SymmetricPowerImpl extends SymmetricPower implements LimitationChangedListener, BridgeEventListener { /* @@ -23,6 +24,8 @@ public class SymmetricPowerImpl extends SymmetricPower implements LimitationChan private List staticLimitations; private List dynamicLimitations; + private AvgFiFoQueue activePowerAvg; + private AvgFiFoQueue reactivePowerAvg; public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePower, WriteChannel setReactivePower) { @@ -31,6 +34,8 @@ public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePow this.dynamicLimitations = new ArrayList<>(); this.setActivePower = setActivePower; this.setReactivePower = setReactivePower; + activePowerAvg = new AvgFiFoQueue(3, 1.5); + reactivePowerAvg = new AvgFiFoQueue(3, 1.5); createBaseGeometry(); reset(); } @@ -69,9 +74,11 @@ private void writePower() { Point p = reduceToZero(); Coordinate c = p.getCoordinate(); setGeometry(p); + activePowerAvg.add((long) c.x); + reactivePowerAvg.add((long) c.y); try { - this.setActivePower.pushWrite((long) c.x); - this.setReactivePower.pushWrite((long) c.y); + this.setActivePower.pushWrite(activePowerAvg.avg()); + this.setReactivePower.pushWrite(reactivePowerAvg.avg()); setActivePower.shadowCopyAndReset(); setReactivePower.shadowCopyAndReset(); } catch (WriteChannelException e) { From 3ea07a40dda91d91ec1d32e8ab50a24114f65c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:40:12 +0100 Subject: [PATCH 048/156] fix symmetricPower implementation --- .../io/openems/impl/device/simulator/SimulatorSymmetricEss.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index 67eed54f971..c168c941f66 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -137,6 +137,8 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol allowedDischargeLimit.setP(allowedDischarge.valueOptional().orElse(0L)); } }); + this.power.addStaticLimitation(this.allowedDischargeLimit); + getParent().getBridge().addListener(this.power); } /* From 9467cc245788f7e9f09ab1d2b8f7d564a9a8a9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:40:23 +0100 Subject: [PATCH 049/156] implement SymmetricPower --- .../system/esscluster/EssClusterNature.java | 245 +----------------- 1 file changed, 11 insertions(+), 234 deletions(-) diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java index de43b638952..a05067c52e5 100644 --- a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java +++ b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java @@ -1,9 +1,7 @@ package io.openems.impl.device.system.esscluster; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; @@ -34,6 +32,8 @@ import io.openems.core.BridgeInitializedEventListener; import io.openems.core.Config; import io.openems.core.ThingRepository; +import io.openems.core.utilities.power.SymmetricPower; +import io.openems.core.utilities.power.SymmetricPowerClusterImpl; import io.openems.impl.protocol.system.SystemDeviceNature; @ThingInfo(title = "Ess Cluster") @@ -50,6 +50,7 @@ public class EssClusterNature extends SystemDeviceNature implements SymmetricEss private ConfigChannel chargeSoc = new ConfigChannel("chargeSoc", this); private List essList = new ArrayList<>(); private boolean isInitialized = false; + private SymmetricPowerClusterImpl power; private FunctionalReadChannel soc = new FunctionalReadChannel("Soc", this, (channels) -> { double nominalKWhSum = 0; double actualCapacity = 0; @@ -273,228 +274,11 @@ public Long setMaxValue(Long newValue, String newLabel, }).label(0L, EssNature.STOP).label(1L, EssNature.START); - private FunctionalWriteChannel setActivePower = new FunctionalWriteChannel("SetActivePower", this, - new FunctionalWriteChannelFunction() { - - @Override - public Long setValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - long minValue = 0L; - boolean minValueValid = false; - long maxValue = 0L; - boolean maxValueValid = false; - Map soc = new HashMap<>(); - for (SymmetricEssNature ess : essList) { - if (ess.soc().valueOptional().isPresent()) { - soc.put(ess.id(), ess.soc().valueOptional().get()); - } else { - soc.put(ess.id(), 0L); - } - if (ess.setActivePower().writeMin().isPresent()) { - minValue += ess.setActivePower().writeMin().get(); - minValueValid = true; - } - if (ess.setActivePower().writeMax().isPresent()) { - maxValue += ess.setActivePower().writeMax().get(); - maxValueValid = true; - } - } - if (maxValueValid && maxValue < newValue) { - throw new WriteChannelException("Value [" + newValue + "] for [" + setActivePower.address() - + "] is out of boundaries. Max value [" + maxValue + "] had already been set"); - } - if (minValueValid && minValue > newValue) { - throw new WriteChannelException("Value [" + newValue + "] for [" + setActivePower.address() - + "] is out of boundaries. Min value [" + minValue + "] had already been set"); - } - for (WriteChannel channel : channels) { - long power = 0L; - if (channels.length > 0) { - if (newValue >= 0) { - power = newValue / (channels.length * 100) * soc.get(channel.parent().id()); - } else { - power = newValue / (channels.length * 100) * (100 - soc.get(channel.parent().id())); - } - } - try { - channel.pushWrite(power); - } catch (WriteChannelException e) { - log.error("Failed to write " + power + " to " + channel.address(), e); - } - } - return newValue; - } - - @Override - public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channels) { - long sum = 0L; - for (ReadChannel channel : channels) { - try { - sum += channel.value(); - } catch (InvalidValueException e) { - log.error("Can't read ActivePower from " + channel.address()); - } - } - return sum; - } - - @Override - public Long getMinValue(Optional minValue, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long min = 0L; - boolean isPresent = false; - for (WriteChannel channelMin : channels) { - if (channelMin.writeMin().isPresent()) { - min += channelMin.writeMin().get(); - isPresent = true; - } - } - if (isPresent) { - return min; - } - return null; - } - - @Override - public Long getMaxValue(Optional maxValue, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long max = 0L; - boolean isPresent = false; - for (WriteChannel channelMax : channels) { - if (channelMax.writeMax().isPresent()) { - max += channelMax.writeMax().get(); - isPresent = true; - } - } - if (isPresent) { - return max; - } - return null; - } - - @Override - public Long setMinValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) { - // don't forward the maxValue otherwise the pushWrite with power weight by soc will break - return newValue; - } - - @Override - public Long setMaxValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) { - // don't forward the maxValue otherwise the pushWrite with power weight by soc will break - return newValue; - } - - }); - private FunctionalWriteChannel setReactivePower = new FunctionalWriteChannel("SetReactivePower", this, - new FunctionalWriteChannelFunction() { - - @Override - public Long setValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long power = 0L; - if (channels.length > 0) { - power = newValue / channels.length; - } - for (WriteChannel channel : channels) { - try { - channel.pushWrite(power); - } catch (WriteChannelException e) { - log.error("Failed to write " + power + " to " + channel.address(), e); - } - } - return newValue; - } - - @Override - public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channels) { - long sum = 0L; - for (ReadChannel channel : channels) { - try { - sum += channel.value(); - } catch (InvalidValueException e) { - log.error("Can't read ReactivePower from " + channel.address()); - } - } - return sum; - } - - @Override - public Long getMinValue(Optional minValue, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long min = 0L; - boolean isPresent = false; - for (WriteChannel channelMin : channels) { - if (channelMin.writeMin().isPresent()) { - min += channelMin.writeMin().get(); - isPresent = true; - } - } - if (isPresent) { - return min; - } - return null; - } - - @Override - public Long getMaxValue(Optional maxValue, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long max = 0L; - boolean isPresent = false; - for (WriteChannel channelMax : channels) { - if (channelMax.writeMax().isPresent()) { - max += channelMax.writeMax().get(); - isPresent = true; - } - } - if (isPresent) { - return max; - } - return null; - - } - - @Override - public Long setMinValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long power = 0L; - if (channels.length > 0) { - power = newValue / channels.length; - } - for (WriteChannel channel : channels) { - try { - channel.pushWriteMin(power); - } catch (WriteChannelException e) { - log.error("Failed to write " + power + " to " + channel.address(), e); - } - } - return newValue; - } - - @Override - public Long setMaxValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) { - long power = 0L; - if (channels.length > 0) { - power = newValue / channels.length; - } - for (WriteChannel channel : channels) { - try { - channel.pushWriteMax(power); - } catch (WriteChannelException e) { - log.error("Failed to write " + power + " to " + channel.address(), e); - } - } - return newValue; - } - - }); - public EssClusterNature(String id, Device parent) throws ConfigException { super(id, parent); this.listeners = new ArrayList<>(); Config.getInstance().addBridgeInitializedEventListener(this); + power = new SymmetricPowerClusterImpl(); } @Override @@ -572,16 +356,6 @@ public ReadChannel maxNominalPower() { return maxNominalPower; } - @Override - public WriteChannel setActivePower() { - return setActivePower; - } - - @Override - public WriteChannel setReactivePower() { - return setReactivePower; - } - @Override public ReadChannel capacity() { return capacity; @@ -623,8 +397,7 @@ private void loadEss() { maxNominalPower.removeChannel(ess.maxNominalPower()); capacity.removeChannel(ess.capacity()); setWorkState.removeChannel(ess.setWorkState()); - setActivePower.removeChannel(ess.setActivePower()); - setReactivePower.removeChannel(ess.setReactivePower()); + power.removeEss(ess); } essList.clear(); if (essIds != null && isInitialized) { @@ -645,8 +418,7 @@ private void loadEss() { maxNominalPower.addChannel(ess.maxNominalPower()); capacity.addChannel(ess.capacity()); setWorkState.addChannel(ess.setWorkState()); - setActivePower.addChannel(ess.setActivePower()); - setReactivePower.addChannel(ess.setReactivePower()); + power.addEss(ess); } } } @@ -682,4 +454,9 @@ public void onBridgeInitialized() { loadEss(); } + @Override + public SymmetricPower getPower() { + return power; + } + } From acf83e8d329b3331f28ddef0b392074bb9fe255b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:40:39 +0100 Subject: [PATCH 050/156] remove average calculation for ess --- .../src/io/openems/impl/controller/symmetric/balancing/Ess.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java index e52952fc33a..051d14cd32b 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java @@ -25,7 +25,6 @@ import io.openems.api.controller.ThingMap; import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.exception.InvalidValueException; -import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.power.PEqualLimitation; import io.openems.core.utilities.power.SymmetricPower; @@ -40,7 +39,6 @@ public class Ess extends ThingMap { public final ReadChannel gridMode; public final ReadChannel systemState; public final SymmetricPower power; - public AvgFiFoQueue powerAvg = new AvgFiFoQueue(1, 1); public final PEqualLimitation limit; public Ess(SymmetricEssNature ess) { From 1c1db12e406a1778596c85d65e881a1417d5591e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 15:41:22 +0100 Subject: [PATCH 051/156] add test application for SymmetricPower --- .../io/openems/core/utilities/power/Test.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 edge/src/io/openems/core/utilities/power/Test.java diff --git a/edge/src/io/openems/core/utilities/power/Test.java b/edge/src/io/openems/core/utilities/power/Test.java new file mode 100644 index 00000000000..59ae65dd168 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/Test.java @@ -0,0 +1,72 @@ +package io.openems.core.utilities.power; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Stopwatch; +import com.vividsolutions.jts.densify.Densifier; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.util.AffineTransformation; + +public class Test { + + public static void main(String[] args) throws PowerException { + Stopwatch sw = Stopwatch.createStarted(); + SymmetricPowerImpl power = new SymmetricPowerImpl(100000, null, null); + SymmetricPowerImpl power2 = new SymmetricPowerImpl(100000, null, null); + System.out.println(sw.elapsed().toMillis()); + sw.reset(); + sw.start(); + QGreaterEqualLimitation limit1 = new QGreaterEqualLimitation(power); + limit1.setQ(-1100L); + power.applyLimitation(limit1); + NoPBetweenLimitation limit2 = new NoPBetweenLimitation(power); + limit2.setP(0L, 5000L); + power2.applyLimitation(limit2); + // CosPhiLineLimitation limit = new CosPhiLineLimitation(power); + // limit.setCosPhi(0.8, true, 0L, 0L); + // power2.applyLimitation(limit); + // MaxCosPhiLimitation limit = new MaxCosPhiLimitation(power); + // limit.setMaxCosPhi(0.8); + // power.applyLimitation(limit); + // CosPhiLineCharacteristicLimitation limit = new CosPhiLineCharacteristicLimitation(power); + // TreeMap characteristic = new TreeMap<>(); + // characteristic.put(-2000L, -0.75); + // characteristic.put(0L, 1.0); + // characteristic.put(2000L, -0.9); + // limit.setCosPhi(0L, 0L, characteristic); + // power.applyLimitation(limit); + System.out.println(sw.elapsed().toMillis()); + sw.reset(); + System.out.println(power.getMaxP()); + System.out.println(power.getMinP()); + System.out.println(power.getMaxQ()); + System.out.println(power.getMinQ()); + System.out.println(power.getAsSVG()); + sw.start(); + Geometry g = getUnionAround(power.getGeometry(), power2.getGeometry()); + System.out.println(sw.elapsed().toMillis()); + System.out.println(SymmetricPowerImpl.getAsSVG(g)); + } + + private static Geometry getUnionAround(Geometry g1,Geometry g2) { + GeometryFactory factory = new GeometryFactory(); + Geometry g1dens = Densifier.densify(g1, 10000); + Geometry g2dens = Densifier.densify(g2, 10000); + List geometries = new ArrayList<>(); + geometries.add(g1); + for (Coordinate c : g1dens.getCoordinates()) { + geometries.add(AffineTransformation.translationInstance(c.x, c.y).transform(g2)); + } + geometries.add(g2); + for (Coordinate c : g2dens.getCoordinates()) { + geometries.add(AffineTransformation.translationInstance(c.x, c.y).transform(g1)); + } + GeometryCollection collection = new GeometryCollection(geometries.toArray(new Geometry[geometries.size()]), factory); + return collection.union(); + } + +} From 3a6bbb7fe0f23e70138331282ed15045461acbbf Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 16:18:40 +0100 Subject: [PATCH 052/156] Enable UiWebsocketService --- io.openems.backend.application/BackendApp.bndrun | 3 ++- .../io/openems/backend/application/BackendApp.java | 5 +++-- .../bnd.bnd | 3 ++- .../edgewebsocket/impl/provider/EdgeWebsocket.java | 4 ++++ io.openems.backend.uiwebsocket.api/bnd.bnd | 6 ++++-- .../uiwebsocket/impl/provider/UiWebsocket.java | 12 ++++++++++-- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 3e391ea14be..8aaadfb0251 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -35,4 +35,5 @@ JPM-Command: openems-backend io.openems.backend.metadata.odoo.provider;version=snapshot,\ io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ - org.osgi.service.event;version='[1.3.1,1.3.2)' \ No newline at end of file + org.osgi.service.event;version='[1.3.1,1.3.2)',\ + io.openems.backend.uiwebsocket.impl.provider;version=snapshot \ No newline at end of file diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 5e35b483758..1844dd2b05d 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -14,6 +14,7 @@ import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.uiwebsocket.api.UiWebsocketService; @Component() public class BackendApp { @@ -32,8 +33,8 @@ public class BackendApp { @Reference EdgeWebsocketService edgeWebsocketService; - // @Reference - // UiWebsocketService uiWebsocketService; + @Reference + UiWebsocketService uiWebsocketService; // @Reference(target = "(component.factory=EdgeWebsocketFactory)") // private ComponentFactory factory; diff --git a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd index 1cb54141b2d..15a58947fef 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd @@ -17,7 +17,8 @@ Export-Package: io.openems.backend.edgewebsocket.api com.google.guava,\ io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ - io.openems.backend.common;version=latest + io.openems.backend.common;version=latest,\ + io.openems.backend.uiwebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index b9fdf55499e..e716d2f2ede 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -12,6 +12,7 @@ import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.uiwebsocket.api.UiWebsocketService; import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @@ -27,6 +28,9 @@ public class EdgeWebsocket implements EdgeWebsocketService { @Reference protected MetadataService metadataService; + @Reference + protected UiWebsocketService uiWebsocketService; + @Reference protected EventAdmin eventAdmin; diff --git a/io.openems.backend.uiwebsocket.api/bnd.bnd b/io.openems.backend.uiwebsocket.api/bnd.bnd index 21b6fc4ea60..74346fb8382 100644 --- a/io.openems.backend.uiwebsocket.api/bnd.bnd +++ b/io.openems.backend.uiwebsocket.api/bnd.bnd @@ -11,10 +11,12 @@ Require-Capability: \ -includeresource: {readme.md} --buildpath: \ - osgi.enroute.base.api;version=2.1 +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + com.google.gson -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 3a3c3280f1b..0edee91792a 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -6,10 +6,13 @@ import org.osgi.service.component.annotations.Component; 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.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonObject; + import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; @@ -31,7 +34,7 @@ protected MetadataService getMetadataService() { return metadataService; } - @Reference + @Reference(cardinality = ReferenceCardinality.OPTIONAL) // avoid recursive dependency private EdgeWebsocketService edgeWebsocketService; protected EdgeWebsocketService getEdgeWebsocketService() { @@ -49,7 +52,6 @@ void activate(Config config) { this.stopServer(); this.startServer(config.port()); - } @Deactivate @@ -80,4 +82,10 @@ private void startServer(int port) { this.server = new UiWebsocketServer(this, port); this.server.start(); } + + @Override + public void handleEdgeReply(int edgeId, JsonObject jMessage) { + // TODO Auto-generated method stub + log.info("TODO handleEdgeReply"); + } } From ae0f463c24a5cf72973695e6c0f90d53021f2ac0 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 16:52:16 +0100 Subject: [PATCH 053/156] Rename Device to Edge --- .../openems/backend/metadata/api/{Device.java => Edge.java} | 6 +++--- .../io/openems/backend/metadata/api/UserDevicesInfo.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/{Device.java => Edge.java} (60%) diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java similarity index 60% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java index e9519cceb8f..25f7d68a241 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Device.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java @@ -1,12 +1,12 @@ package io.openems.backend.metadata.api; -public class Device { +public class Edge { private final int id; private String name; private String comment; private String producttype; - public Device(int id, String name, String comment, String producttype) { + public Edge(int id, String name, String comment, String producttype) { this.id = id; this.name = name; this.comment = comment; @@ -19,6 +19,6 @@ public int getId() { @Override public String toString() { - return "Device [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype + "]"; + return "Edge [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype + "]"; } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java index 58e384fd55b..655240da9a3 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserDevicesInfo.java @@ -6,14 +6,14 @@ public class UserDevicesInfo { private final User user; - private final Map devices = new HashMap<>(); + private final Map devices = new HashMap<>(); public UserDevicesInfo(User user) { super(); this.user = user; } - public void addDevice(Device device) { + public void addDevice(Edge device) { this.devices.put(device.getId(), device); } @@ -21,7 +21,7 @@ public User getUser() { return user; } - public Map getDevices() { + public Map getDevices() { return devices; } From 757ad555a04bd591f4f54451b08c0d386059bdb2 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 16:52:44 +0100 Subject: [PATCH 054/156] Minor improvements --- .../backend/application/BackendApp.java | 1 + .../provider/AbstractWebsocketServer.java | 8 +-- .../impl/provider/EdgeWebsocket.java | 3 +- .../impl/provider/EdgeWebsocketServer.java | 58 +++++++++++++++---- .../backend/metadata/api/MetadataService.java | 6 +- .../openems/backend/metadata/odoo/Odoo.java | 24 ++++++-- .../uiwebsocket/api/UiWebsocketService.java | 4 ++ .../impl/provider/UiWebsocketServer.java | 37 ++++++------ 8 files changed, 100 insertions(+), 41 deletions(-) diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 1844dd2b05d..a8c7e9658e8 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -77,6 +77,7 @@ private void configureLogging() { // set minimum log levels for some verbose packages log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); log4jProps.put("log4j.logger.org.apache.felix.configadmin", "INFO"); + log4jProps.put("log4j.logger.sun.net.www.protocol.http.HttpURLConnection", "INFO"); configuration.update(log4jProps); } catch (IOException e1) { e1.printStackTrace(); diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java index 66dc78d8336..1a24bcc61bd 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java @@ -13,7 +13,6 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -23,8 +22,7 @@ public abstract class AbstractWebsocketServer extends WebSocketServer { private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); - protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt); + protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage); protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); @@ -61,9 +59,7 @@ public final void onOpen(WebSocket websocket, ClientHandshake handshake) { public final void onMessage(WebSocket websocket, String message) { try { JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); - Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); - Optional deviceNameOpt = JsonUtils.getAsOptionalString(jMessage, "device"); - this._onMessage(websocket, jMessage, jMessageIdOpt, deviceNameOpt); + this._onMessage(websocket, jMessage); } catch (Throwable e) { log.error("onMessage-Error [" + message + "]: " + e.getMessage()); e.printStackTrace(); diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index e716d2f2ede..2a28e9032ed 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -6,6 +6,7 @@ import org.osgi.service.component.annotations.Component; 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.event.EventAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +29,7 @@ public class EdgeWebsocket implements EdgeWebsocketService { @Reference protected MetadataService metadataService; - @Reference + @Reference(cardinality = ReferenceCardinality.OPTIONAL) // avoid recursive dependency protected UiWebsocketService uiWebsocketService; @Reference diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index a8dc719bcf5..3cd51f2ca9b 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -11,12 +11,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.openems.backend.common.events.BackendEventConstants; -import io.openems.backend.metadata.api.Device; +import io.openems.backend.metadata.api.Edge; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; import io.openems.common.websocket.DefaultMessages; import io.openems.common.websocket.WebSocketUtils; @@ -31,6 +32,9 @@ public EdgeWebsocketServer(EdgeWebsocket parent, int port) { this.parent = parent; } + /** + * Open event of websocket. Parses the "apikey" and to authenticate Edge. + */ @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { String apikey = ""; @@ -75,7 +79,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { // log for (int edgeId : edgeIds) { - Optional deviceOpt = this.parent.metadataService.getDevice(edgeId); + Optional deviceOpt = this.parent.metadataService.getEdge(edgeId); if (deviceOpt.isPresent()) { log.info("Device [" + deviceOpt.get() + "] connected."); } else { @@ -108,10 +112,46 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { } } + /** + * Message event of websocket. Handles a new message. At this point the device + * is already authenticated. + */ @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - System.out.println("_onMessage"); + protected void _onMessage(WebSocket websocket, JsonObject jMessage) { + log.info("message: " + StringUtils.toShortString(jMessage, 100)); + // get edgeIds from websocket + int[] edgeIds = websocket.getAttachment(); + + /* + * Config? -> store in Metadata + */ + if (jMessage.has("config")) { + try { + JsonObject jConfig = JsonUtils.getAsJsonObject(jMessage, "config"); + for (int edgeId : edgeIds) { + this.parent.metadataService.updateEdgeConfig(edgeId, jConfig); + } + } catch (OpenemsException e) { + log.error("Device [IDs:" + edgeIds + "] sent config. Unable to parse: " + e.getMessage()); + } + } + + /* + * Is this a reply? -> forward to Browser + */ + if (jMessage.has("id")) { + for (int edgeId : edgeIds) { + this.parent.uiWebsocketService.handleEdgeReply(edgeId, jMessage); + } + } + + /* + * New timestamped data + */ + if (jMessage.has("timedata")) { + log.info("TODO: timedata"); + // TODO timedata(devices, jMessage.get("timedata")); + } } @Override @@ -121,8 +161,6 @@ protected void _onError(WebSocket websocket, Exception ex) { @Override protected void _onClose(WebSocket websocket) { - System.out.println("_onClose"); - // get edgeIds from websocket int[] edgeIds = websocket.getAttachment(); @@ -140,10 +178,10 @@ protected void _onClose(WebSocket websocket) { Event event = new Event(BackendEventConstants.TOPIC_EDGE_OFFLINE, properties); this.parent.eventAdmin.postEvent(event); } - + // log for (int edgeId : edgeIds) { - Optional deviceOpt = this.parent.metadataService.getDevice(edgeId); + Optional deviceOpt = this.parent.metadataService.getEdge(edgeId); if (deviceOpt.isPresent()) { log.info("Device [" + deviceOpt.get() + "] disconnected."); } else { diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 528269d7eda..810dba79b32 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -4,6 +4,8 @@ import org.osgi.annotation.versioning.ProviderType; +import com.google.gson.JsonObject; + import io.openems.common.exceptions.OpenemsException; @ProviderType @@ -13,6 +15,8 @@ public interface MetadataService { public abstract int[] getEdgeIdsForApikey(String apikey); - public abstract Optional getDevice(int edgeId); + public abstract Optional getEdge(int edgeId); + public abstract void updateEdgeConfig(int edgeId, JsonObject jConfig); + } diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 466a52d33bf..44706cc261f 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -9,6 +9,8 @@ import java.net.URL; import java.net.URLEncoder; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -24,7 +26,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import io.openems.backend.metadata.api.Device; +import io.openems.backend.metadata.api.Edge; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.metadata.api.Role; import io.openems.backend.metadata.api.User; @@ -53,6 +55,9 @@ public class Odoo implements MetadataService { private String uid; private String password; + private ConcurrentMap users = new ConcurrentHashMap<>(); + private ConcurrentMap edges = new ConcurrentHashMap<>(); + @Activate void activate(Config config) { log.debug("Activate Odoo [url=" + config.url() + ";database=" + config.database() + ";uid=" + config.uid() @@ -116,15 +121,15 @@ public User getUserWithSession(String sessionId) throws OpenemsException { JsonUtils.getAsString(jUser, "name")); JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); for (JsonElement jDevice : jDevices) { - Device device = new Device(// + Edge edge = new Edge(// JsonUtils.getAsInt(jDevice, "id"), // JsonUtils.getAsString(jDevice, "name"), // JsonUtils.getAsString(jDevice, "comment"), // JsonUtils.getAsString(jDevice, "producttype")); - // this.devices.putIfAbsent(device.getId(), device); - user.addDeviceRole(device.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); + this.edges.putIfAbsent(edge.getId(), edge); + user.addDeviceRole(edge.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); } - // this.users.put(user.getId(), user); + this.users.put(user.getId(), user); return user; } } @@ -145,11 +150,18 @@ public int[] getEdgeIdsForApikey(String apikey) { } @Override - public Optional getDevice(int edgeId) { + public Optional getEdge(int edgeId) { // TODO Auto-generated method stub + log.info("TODO: getEdge"); return Optional.empty(); } + @Override + public void updateEdgeConfig(int edgeId, JsonObject jConfig) { + // TODO Auto-generated method stub + log.info("TODO: updateEdgeConfig"); + } + // public Optional getUser(int id) { // return Optional.ofNullable(this.users.get(id)); // } diff --git a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java index 80f82ff3dd1..c589adb8871 100644 --- a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java +++ b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java @@ -2,7 +2,11 @@ import org.osgi.annotation.versioning.ProviderType; +import com.google.gson.JsonObject; + @ProviderType public interface UiWebsocketService { + public abstract void handleEdgeReply(int edgeId, JsonObject jMessage); + } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index f5d0188bd13..1dbd3df5fb9 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -29,6 +29,7 @@ public UiWebsocketServer(UiWebsocket parent, int port) { @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + log.info("OnOpen"); String error = ""; User user = null; @@ -51,24 +52,26 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { websocket.closeConnection(CloseFrame.REFUSE, error); } else if (user != null) { // send connection successful to browser - for(Entry deviceRole : user.getDeviceRoles().entrySet()) { -// boolean isOnline = this.parent.getEdgeWebsocketService().isOnline(deviceRole.getKey()); -// JsonArray jDevices + for (Entry deviceRole : user.getDeviceRoles().entrySet()) { + // boolean isOnline = + // this.parent.getEdgeWebsocketService().isOnline(deviceRole.getKey()); + // JsonArray jDevices } - -// -// -// JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), -// Optional.empty(), -// data.getDevices()); -// // TODO write user name to log output -// WebSocketUtils.send(websocket, jReply); -// -// // add websocket to local cache -// this.addWebsocket(websocket, session); -// log.info("User [" + data.getUserName() + "] connected with Session [" + -// data.getOdooSessionId().orElse("") -// + "]."); + + // + // + // JsonObject jReply = + // DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), + // Optional.empty(), + // data.getDevices()); + // // TODO write user name to log output + // WebSocketUtils.send(websocket, jReply); + // + // // add websocket to local cache + // this.addWebsocket(websocket, session); + // log.info("User [" + data.getUserName() + "] connected with Session [" + + // data.getOdooSessionId().orElse("") + // + "]."); } From f88d570559240e5443a24beaee1f7475912422b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Feb 2018 16:52:45 +0100 Subject: [PATCH 055/156] fix errors after merge --- edge/.classpath | 21 ---- ...BalancingBandgapActivePowerController.java | 7 ++ ...lancingBandgapReactivePowerController.java | 3 +- .../BalancingOffsetActivePowerController.java | 7 ++ ...alancingOffsetReactivePowerController.java | 7 ++ .../FixValueActivePowerController.java | 7 ++ .../ActivePowerLimitationController.java | 7 ++ .../ReactivePowerLimitationController.java | 3 +- ...ePowerVoltageCharacteristicController.java | 3 +- ...ePowerVoltageCharacteristicController.java | 7 ++ .../alwayson/AlwaysOnController.java | 7 ++ .../powerthreshold/ThresholdOnController.java | 7 ++ .../systemstate/time/TimeOnController.java | 7 ++ .../impl/device/byd/Bem125ktla01Ess.java | 79 ++++++------- .../commercial/FeneconCommercialEss.java | 3 +- .../impl/device/mini/FeneconMiniEss.java | 3 +- .../io/openems/impl/device/refu/RefuEss.java | 104 +++++++++--------- .../simulator/SimulatorSymmetricEss.java | 2 +- .../impl/device/sma/SunnyIsland6Ess.java | 2 +- ...ymmetricSymmetricCombinationEssNature.java | 2 +- .../system/esscluster/EssClusterNature.java | 2 +- .../impl/protocol/modbus/ModbusRtu.java | 2 + .../impl/scheduler/SimpleScheduler.java | 1 + .../ChannelThresholdScheduler.java | 1 + .../scheduler/time/WeekTimeScheduler.java | 1 + .../UnitTestSymmetricEssNature.java | 7 +- 26 files changed, 180 insertions(+), 122 deletions(-) diff --git a/edge/.classpath b/edge/.classpath index 6b24e6b56f4..8577b7e5109 100644 --- a/edge/.classpath +++ b/edge/.classpath @@ -1,4 +1,3 @@ -<<<<<<< HEAD @@ -31,23 +30,3 @@ -======= - - - - - - - - - - - - - - - - - - ->>>>>>> origin/feature/ErrorChannels diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java index d7fec9165d1..a1834e88c85 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java @@ -21,6 +21,7 @@ package io.openems.impl.controller.symmetric.balancingbandgap; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -30,6 +31,7 @@ @ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.") public class BalancingBandgapActivePowerController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -82,4 +84,9 @@ public void run() { } } + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } + } diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java index 5e05ed9f083..5ea9d1c5f4e 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java @@ -23,6 +23,7 @@ import java.util.NoSuchElementException; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -85,7 +86,7 @@ public void run() { log.error("failed to set ReactivePower!", e); } } - + @Override public ThingStateChannel getStateChannel() { return this.thingState; diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java index c95fd2487ed..648849f9545 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java @@ -21,6 +21,7 @@ package io.openems.impl.controller.symmetric.balancingoffset; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -33,6 +34,7 @@ @ThingInfo(title = "Balancing offset (Symmetric)", description = "Tries to keep the grid meter within an offset. For symmetric Ess.") public class BalancingOffsetActivePowerController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -78,4 +80,9 @@ public void run() { } } + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } + } diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java index 908005e8bfb..187144e8edc 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java @@ -21,6 +21,7 @@ package io.openems.impl.controller.symmetric.balancingoffset; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -33,6 +34,7 @@ @ThingInfo(title = "Balancing offset (Symmetric)", description = "Tries to keep the grid meter within an offset. For symmetric Ess.") public class BalancingOffsetReactivePowerController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -75,4 +77,9 @@ public void run() { } } + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } + } diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java index e0c901171b7..7a9905fb738 100644 --- a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java @@ -23,6 +23,7 @@ import java.util.List; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -32,6 +33,7 @@ @ThingInfo(title = "Fixed active and reactive power (Symmetric)", description = "Charges or discharges the battery with a predefined, fixed power. For symmetric Ess.") public class FixValueActivePowerController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -69,4 +71,9 @@ public void run() { } } + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } + } diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java index ebd76d75c44..e9d03864b89 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java @@ -21,6 +21,7 @@ package io.openems.impl.controller.symmetric.powerlimitation; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -30,6 +31,7 @@ @ThingInfo(title = "Power limitation (Symmetric)", description = "Limits the active and reactive power of the Ess. For symmetric Ess.") public class ActivePowerLimitationController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -81,4 +83,9 @@ public void run() { } } + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } + } diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java index 8166a81ee55..d8cb5ee2e47 100644 --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java @@ -21,6 +21,7 @@ package io.openems.impl.controller.symmetric.powerlimitation; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -87,7 +88,7 @@ public void run() { log.error("No ess found.", e); } } - + @Override public ThingStateChannel getStateChannel() { return this.thingState; diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java index 5bce5c14c4e..c98e30e4cf8 100644 --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java @@ -27,6 +27,7 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -111,7 +112,7 @@ public void run() { } } -@Override + @Override public ThingStateChannel getStateChannel() { return this.thingState; } diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java index 066e09a9d48..dad3c01fc70 100644 --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java @@ -27,6 +27,7 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; @@ -38,6 +39,7 @@ @ThingInfo(title = "Voltage characteristics (Symmetric)") public class ReactivePowerVoltageCharacteristicController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -109,4 +111,9 @@ public void run() { } } + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } + } diff --git a/edge/src/io/openems/impl/controller/systemstate/alwayson/AlwaysOnController.java b/edge/src/io/openems/impl/controller/systemstate/alwayson/AlwaysOnController.java index aa50064fc11..d8757469943 100644 --- a/edge/src/io/openems/impl/controller/systemstate/alwayson/AlwaysOnController.java +++ b/edge/src/io/openems/impl/controller/systemstate/alwayson/AlwaysOnController.java @@ -23,6 +23,7 @@ import java.util.Set; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.device.nature.ess.EssNature; import io.openems.api.doc.ChannelInfo; @@ -36,6 +37,7 @@ @ThingInfo(title = "Keep always running", description = "Tries to keep the Ess always running. Use if Off-Grid functionality is required.") public class AlwaysOnController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -70,4 +72,9 @@ public void run() { log.error("No Storage Found!", e); } } + + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } } diff --git a/edge/src/io/openems/impl/controller/systemstate/powerthreshold/ThresholdOnController.java b/edge/src/io/openems/impl/controller/systemstate/powerthreshold/ThresholdOnController.java index 1310fe90de0..59448497b86 100644 --- a/edge/src/io/openems/impl/controller/systemstate/powerthreshold/ThresholdOnController.java +++ b/edge/src/io/openems/impl/controller/systemstate/powerthreshold/ThresholdOnController.java @@ -25,6 +25,7 @@ import com.google.common.base.Optional; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.device.nature.ess.EssNature; import io.openems.api.doc.ChannelInfo; @@ -39,6 +40,7 @@ @ThingInfo(title = "Stop if not useable", description = "Starts the ess if the GridFeed power is lager than a defined threshold. The ess will be stoped if the ess are empty and the GridFeed power is below a defined threshold.") public class ThresholdOnController extends Controller { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -130,4 +132,9 @@ public void run() { log.error("No Storage Found!", e); } } + + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } } diff --git a/edge/src/io/openems/impl/controller/systemstate/time/TimeOnController.java b/edge/src/io/openems/impl/controller/systemstate/time/TimeOnController.java index fd2ba843568..ef3047e16b1 100644 --- a/edge/src/io/openems/impl/controller/systemstate/time/TimeOnController.java +++ b/edge/src/io/openems/impl/controller/systemstate/time/TimeOnController.java @@ -27,6 +27,7 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.device.nature.ess.EssNature; import io.openems.api.doc.ChannelInfo; @@ -41,6 +42,7 @@ @ThingInfo(title = "Keep always running", description = "Tries to keep the Ess always running. Use if Off-Grid functionality is required.") public class TimeOnController extends Controller implements ChannelChangeListener { + private ThingStateChannel thingState = new ThingStateChannel(this); /* * Constructors */ @@ -144,4 +146,9 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } } } + + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } } diff --git a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java index 58d87b7948c..db83fc51f33 100644 --- a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java +++ b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java @@ -23,14 +23,14 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; -import io.openems.api.channel.StatusBitChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; import io.openems.core.utilities.power.SymmetricPowerImpl; +import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadChannel; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; @@ -205,41 +205,41 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .warningBit(9, WarningEss.FailureOfHumiditySensorInControlCabinet) // Failure_of_humidity_sensor_in_control_cabinet .warningBit(12,WarningEss.FailureOfStorageDevice) // Failure_of_storage_device .warningBit(13,WarningEss.ExceedingOfHumidityInControlCabinet)) // Exceeding_of_humidity_in_control_cabinet - ), new ModbusRegisterRange(0x1300, new UnsignedWordElement(0x1300, // - batteryStackVoltage = new ModbusReadLongChannel("BatteryStackVoltage", this) - .multiplier(2).unit("mV")), - new UnsignedWordElement(0x1301, // - batteryStackCurrent = new ModbusReadLongChannel("BatteryStackCurrent", this) - .multiplier(2).unit("mA")), - new UnsignedWordElement(0x1302, // - batteryStackPower = new ModbusReadLongChannel("BatteryStackPower", this) - .multiplier(2).unit("W")), - new UnsignedWordElement(0x1303, // - batteryStackSoc = soc = new ModbusReadLongChannel("BatteryStackSoc", this) - .unit("%")), - new UnsignedWordElement(0x1304, // - batteryStackSoh = new ModbusReadLongChannel("BatteryStackSoh", this).unit("%")), - new UnsignedWordElement(0x1305, // - batteryStackMaxChargeCurrent = new ModbusReadLongChannel( - "BatteryStackMaxChargeCurrent", this).multiplier(2).unit("mA")), - new UnsignedWordElement(0x1306, // - batteryStackMaxDischargeCurrent = new ModbusReadLongChannel( - "BatteryStackMaxDischargeCurrent", this).multiplier(2).unit("mA")), - new UnsignedWordElement(0x1307, // - batteryStackMaxChargePower = new ModbusReadLongChannel( - "BatteryStackMaxChargePower", this).multiplier(2).unit("W")), - new UnsignedWordElement(0x1308, // - batteryStackMaxDischargePower = new ModbusReadLongChannel( - "BatteryStackMaxDischargePower", this).multiplier(2).unit("W")), - new UnsignedWordElement(0x1309, // - batteryStackTotalCapacity = new ModbusReadLongChannel( - "BatteryStackTotalCapacity", this).unit("Wh")), - new UnsignedDoublewordElement(0x130A, // - batteryStackTotalCharge = new ModbusReadLongChannel("BatteryStackTotalCharge", - this).unit("kWh")), - new UnsignedDoublewordElement(0x130C, // - batteryStackTotalDischarge = new ModbusReadLongChannel( - "BatteryStackTotalDischarge", this).unit("kWh")))); + ), new ModbusRegisterRange(0x1300, new UnsignedWordElement(0x1300, // + batteryStackVoltage = new ModbusReadLongChannel("BatteryStackVoltage", this) + .multiplier(2).unit("mV")), + new UnsignedWordElement(0x1301, // + batteryStackCurrent = new ModbusReadLongChannel("BatteryStackCurrent", this) + .multiplier(2).unit("mA")), + new UnsignedWordElement(0x1302, // + batteryStackPower = new ModbusReadLongChannel("BatteryStackPower", this) + .multiplier(2).unit("W")), + new UnsignedWordElement(0x1303, // + batteryStackSoc = soc = new ModbusReadLongChannel("BatteryStackSoc", this) + .unit("%")), + new UnsignedWordElement(0x1304, // + batteryStackSoh = new ModbusReadLongChannel("BatteryStackSoh", this).unit("%")), + new UnsignedWordElement(0x1305, // + batteryStackMaxChargeCurrent = new ModbusReadLongChannel( + "BatteryStackMaxChargeCurrent", this).multiplier(2).unit("mA")), + new UnsignedWordElement(0x1306, // + batteryStackMaxDischargeCurrent = new ModbusReadLongChannel( + "BatteryStackMaxDischargeCurrent", this).multiplier(2).unit("mA")), + new UnsignedWordElement(0x1307, // + batteryStackMaxChargePower = new ModbusReadLongChannel( + "BatteryStackMaxChargePower", this).multiplier(2).unit("W")), + new UnsignedWordElement(0x1308, // + batteryStackMaxDischargePower = new ModbusReadLongChannel( + "BatteryStackMaxDischargePower", this).multiplier(2).unit("W")), + new UnsignedWordElement(0x1309, // + batteryStackTotalCapacity = new ModbusReadLongChannel( + "BatteryStackTotalCapacity", this).unit("Wh")), + new UnsignedDoublewordElement(0x130A, // + batteryStackTotalCharge = new ModbusReadLongChannel("BatteryStackTotalCharge", + this).unit("kWh")), + new UnsignedDoublewordElement(0x130C, // + batteryStackTotalDischarge = new ModbusReadLongChannel( + "BatteryStackTotalDischarge", this).unit("kWh")))); this.power = new SymmetricPowerImpl(125000, setActivePower, setReactivePower); return protocol; } @@ -253,4 +253,9 @@ public StaticValueChannel capacity() { public SymmetricPowerImpl getPower() { return power; } + + @Override + public ThingStateChannel getStateChannel() { + return thingState; + } } diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java index 2e37ab7edd2..1b431df6e0c 100644 --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java @@ -28,7 +28,7 @@ import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; import io.openems.api.channel.StatusBitChannel; -import io.openems.api.channel.StatusBitChannels; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ThingInfo; @@ -38,6 +38,7 @@ import io.openems.core.utilities.power.PSmallerEqualLimitation; import io.openems.core.utilities.power.SMaxLimitation; import io.openems.core.utilities.power.SymmetricPowerImpl; +import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; diff --git a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java index ef372be1d34..3cd83d85709 100644 --- a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java +++ b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java @@ -29,8 +29,8 @@ import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; import io.openems.api.channel.StatusBitChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.EssNature; import io.openems.api.device.nature.ess.SymmetricEssNature; @@ -43,6 +43,7 @@ import io.openems.core.utilities.power.PSmallerEqualLimitation; import io.openems.core.utilities.power.SMaxLimitation; import io.openems.core.utilities.power.SymmetricPowerImpl; +import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; diff --git a/edge/src/io/openems/impl/device/refu/RefuEss.java b/edge/src/io/openems/impl/device/refu/RefuEss.java index ee784a6dece..af6e0f4f797 100644 --- a/edge/src/io/openems/impl/device/refu/RefuEss.java +++ b/edge/src/io/openems/impl/device/refu/RefuEss.java @@ -28,8 +28,8 @@ import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; import io.openems.api.channel.StatusBitChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.AsymmetricEssNature; import io.openems.api.device.nature.ess.SymmetricEssNature; @@ -39,6 +39,7 @@ import io.openems.core.utilities.power.PGreaterEqualLimitation; import io.openems.core.utilities.power.PSmallerEqualLimitation; import io.openems.core.utilities.power.SymmetricPowerImpl; +import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel; import io.openems.impl.protocol.modbus.ModbusDeviceNature; import io.openems.impl.protocol.modbus.ModbusReadLongChannel; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; @@ -579,62 +580,61 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { ), // new UnsignedWordElement(0x143, - batteryFault13 = warning.channel(new StatusBitChannel("BatteryFault13", this)// - )), new UnsignedWordElement(0x144, - batteryFault14 = warning.channel(new StatusBitChannel("BatteryFault14", this)// - )), new UnsignedWordElement(0x145, batteryGroupControlStatus = warning - .channel(new StatusBitChannel("BatteryGroupControlStatus", this)// - )), new UnsignedWordElement(0x146, - errorLog1 = warning.channel(new StatusBitChannel("ErrorLog1", this)// - )), new UnsignedWordElement(0x147, - errorLog2 = warning.channel(new StatusBitChannel("ErrorLog2", this)// - )), new UnsignedWordElement(0x148, - errorLog3 = warning.channel(new StatusBitChannel("ErrorLog3", this)// - )), + batteryFault13 = new StatusBitChannel("BatteryFault13", this)// + ), new UnsignedWordElement(0x144, + batteryFault14 = new StatusBitChannel("BatteryFault14", this)// + ), new UnsignedWordElement(0x145, batteryGroupControlStatus = new StatusBitChannel("BatteryGroupControlStatus", this)// + ), new UnsignedWordElement(0x146, + errorLog1 = new StatusBitChannel("ErrorLog1", this)// + ), new UnsignedWordElement(0x147, + errorLog2 = new StatusBitChannel("ErrorLog2", this)// + ), new UnsignedWordElement(0x148, + errorLog3 = new StatusBitChannel("ErrorLog3", this)// + ), new UnsignedWordElement(0x149, - errorLog4 = warning.channel(new StatusBitChannel("ErrorLog4", this)// - )), new UnsignedWordElement(0x14a, - errorLog5 = warning.channel(new StatusBitChannel("ErrorLog5", this)// - )), new UnsignedWordElement(0x14b, - errorLog6 = warning.channel(new StatusBitChannel("ErrorLog6", this)// - )), + errorLog4 = new StatusBitChannel("ErrorLog4", this)// + ), new UnsignedWordElement(0x14a, + errorLog5 = new StatusBitChannel("ErrorLog5", this)// + ), new UnsignedWordElement(0x14b, + errorLog6 = new StatusBitChannel("ErrorLog6", this)// + ), new UnsignedWordElement(0x14c, - errorLog7 = warning.channel(new StatusBitChannel("ErrorLog7", this)// - )), new UnsignedWordElement(0x14d, - errorLog8 = warning.channel(new StatusBitChannel("ErrorLog8", this)// - )), new UnsignedWordElement(0x14e, - errorLog9 = warning.channel(new StatusBitChannel("ErrorLog9", this)// - )), + errorLog7 = new StatusBitChannel("ErrorLog7", this)// + ), new UnsignedWordElement(0x14d, + errorLog8 = new StatusBitChannel("ErrorLog8", this)// + ), new UnsignedWordElement(0x14e, + errorLog9 = new StatusBitChannel("ErrorLog9", this)// + ), new UnsignedWordElement(0x14f, - errorLog10 = warning.channel(new StatusBitChannel("ErrorLog10", this)// - )), new UnsignedWordElement(0x150, - errorLog11 = warning.channel(new StatusBitChannel("ErrorLog11", this)// - )), new UnsignedWordElement(0x151, - errorLog12 = warning.channel(new StatusBitChannel("ErrorLog12", this)// - )), + errorLog10 = new StatusBitChannel("ErrorLog10", this)// + ), new UnsignedWordElement(0x150, + errorLog11 = new StatusBitChannel("ErrorLog11", this)// + ), new UnsignedWordElement(0x151, + errorLog12 = new StatusBitChannel("ErrorLog12", this)// + ), new UnsignedWordElement(0x152, - errorLog13 = warning.channel(new StatusBitChannel("ErrorLog13", this)// - )), new UnsignedWordElement(0x153, - errorLog14 = warning.channel(new StatusBitChannel("ErrorLog14", this)// - )), new UnsignedWordElement(0x154, - errorLog15 = warning.channel(new StatusBitChannel("ErrorLog15", this)// - )), + errorLog13 = new StatusBitChannel("ErrorLog13", this)// + ), new UnsignedWordElement(0x153, + errorLog14 = new StatusBitChannel("ErrorLog14", this)// + ), new UnsignedWordElement(0x154, + errorLog15 = new StatusBitChannel("ErrorLog15", this)// + ), new UnsignedWordElement(0x155, - errorLog16 = warning.channel(new StatusBitChannel("ErrorLog16", this)// - ))), new WriteableModbusRegisterRange(0x200, // - new UnsignedWordElement(0x200, // - setWorkState = new ModbusWriteLongChannel("SetWorkState", this) // - .label(0, STOP) // - .label(1, START)), - new UnsignedWordElement(0x201, // - setSystemErrorReset = new ModbusWriteLongChannel("SetSystemErrorReset", - this)// - .label(0, OFF)// - .label(1, ON)), - new UnsignedWordElement(0x202, // - setOperationMode = new ModbusWriteLongChannel("SetOperationMode", this)// - .label(0, "P/Q Set point")// - .label(1, "IAC / cosphi set point"))), + errorLog16 = new StatusBitChannel("ErrorLog16", this)// + )), new WriteableModbusRegisterRange(0x200, // + new UnsignedWordElement(0x200, // + setWorkState = new ModbusWriteLongChannel("SetWorkState", this) // + .label(0, STOP) // + .label(1, START)), + new UnsignedWordElement(0x201, // + setSystemErrorReset = new ModbusWriteLongChannel("SetSystemErrorReset", + this)// + .label(0, OFF)// + .label(1, ON)), + new UnsignedWordElement(0x202, // + setOperationMode = new ModbusWriteLongChannel("SetOperationMode", this)// + .label(0, "P/Q Set point")// + .label(1, "IAC / cosphi set point"))), new WriteableModbusRegisterRange(0x203, new SignedWordElement(0x203, // setActivePower = new ModbusWriteLongChannel("SetActivePower", this)// .unit("W").multiplier(2))), diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index 7aca26f9810..cc9edf89112 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -36,8 +36,8 @@ import io.openems.api.channel.FunctionalReadChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.charger.ChargerNature; import io.openems.api.device.nature.ess.EssNature; diff --git a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java index b512a7f6986..1af967452a5 100644 --- a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java +++ b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java @@ -7,8 +7,8 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ChannelInfo; diff --git a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java index 150fe09fe8e..8ef95476199 100644 --- a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java +++ b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java @@ -15,8 +15,8 @@ import io.openems.api.channel.FunctionalWriteChannelFunction; import io.openems.api.channel.ProxyReadChannel; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.AsymmetricEssNature; import io.openems.api.device.nature.ess.EssNature; diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java index a8ae68ce871..60d3f14c856 100644 --- a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java +++ b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java @@ -17,8 +17,8 @@ import io.openems.api.channel.FunctionalWriteChannel; import io.openems.api.channel.FunctionalWriteChannelFunction; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.DeviceNature; import io.openems.api.device.nature.ess.EssNature; diff --git a/edge/src/io/openems/impl/protocol/modbus/ModbusRtu.java b/edge/src/io/openems/impl/protocol/modbus/ModbusRtu.java index fbb065ddbc0..dbeebff18c3 100644 --- a/edge/src/io/openems/impl/protocol/modbus/ModbusRtu.java +++ b/edge/src/io/openems/impl/protocol/modbus/ModbusRtu.java @@ -31,9 +31,11 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ChannelUpdateListener; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.StaticValueChannel; import io.openems.api.device.Device; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.ConfigException; import io.openems.api.exception.OpenemsModbusException; @ThingInfo(title = "Modbus/RTU") diff --git a/edge/src/io/openems/impl/scheduler/SimpleScheduler.java b/edge/src/io/openems/impl/scheduler/SimpleScheduler.java index 1628a95fbec..7d458490e5c 100644 --- a/edge/src/io/openems/impl/scheduler/SimpleScheduler.java +++ b/edge/src/io/openems/impl/scheduler/SimpleScheduler.java @@ -25,6 +25,7 @@ import java.util.List; import info.faljse.SDNotify.SDNotify; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ThingInfo; import io.openems.api.scheduler.Scheduler; diff --git a/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java b/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java index 590156b46e1..16e5ab082b8 100644 --- a/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java +++ b/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java @@ -33,6 +33,7 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; diff --git a/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java b/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java index 7f1d67eb39b..c6c81d4e444 100644 --- a/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java +++ b/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java @@ -35,6 +35,7 @@ import info.faljse.SDNotify.SDNotify; import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java index 829985d785b..d5178db92dd 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java @@ -8,8 +8,8 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.StaticValueChannel; -import io.openems.api.channel.StatusBitChannels; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; import io.openems.api.device.Device; import io.openems.api.device.nature.ess.EssNature; import io.openems.api.device.nature.ess.SymmetricEssNature; @@ -41,9 +41,11 @@ public class UnitTestSymmetricEssNature implements SymmetricEssNature { SimulatorTools.getRandomLong(3000, 50000)); public SymmetricPowerImpl power = new SymmetricPowerImpl(9000, setActivePower, setReactivePower); private final String id; + private ThingStateChannel thingState; public UnitTestSymmetricEssNature(String id) { this.id = id; + thingState = new ThingStateChannel(this); } @Override @@ -157,8 +159,7 @@ public SymmetricPowerImpl getPower() { @Override public ThingStateChannel getStateChannel() { - // TODO Auto-generated method stub - return null; + return thingState; } } From 8cac58b1b73e9b8bb5a0d84e4f95ed1e607d0eb2 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 16:52:54 +0100 Subject: [PATCH 056/156] Fix Angular Toaster --- ui/src/app/shared/shared.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/app/shared/shared.module.ts b/ui/src/app/shared/shared.module.ts index ddb788575e6..4f925bed93e 100644 --- a/ui/src/app/shared/shared.module.ts +++ b/ui/src/app/shared/shared.module.ts @@ -9,7 +9,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts'; import { LoadingModule } from 'ngx-loading'; import { TranslateModule } from '@ngx-translate/core'; import { MyDateRangePickerModule } from 'mydaterangepicker'; -import { ToasterModule } from 'angular2-toaster'; +import { ToasterModule, ToasterService } from 'angular2-toaster'; import 'hammerjs'; @@ -99,6 +99,7 @@ import { ChannelComponent } from './config/channel.component'; Utils, Service, Websocket, + ToasterService, appRoutingProviders ] }) From ed1914560587a086030dbf7aa039791bd96c5782 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Feb 2018 18:00:01 +0100 Subject: [PATCH 057/156] Day-end commit... --- .../org.eclipse.core.resources.prefs | 1 + .../backend/application/BackendApp.java | 12 +++-- .../backend/application/EdgeEventHandler.java | 20 -------- .../backend/application/package-info.java | 2 + .../api/EdgeWebsocketService.java | 2 + .../impl/provider/EdgeWebsocket.java | 8 ++- .../impl/provider/EdgeWebsocketServer.java | 4 ++ .../io/openems/backend/metadata/api/Edge.java | 28 +++++++++- .../metadata/api/EdgeOnlineHandler.java | 47 +++++++++++++++++ .../io/openems/backend/metadata/api/User.java | 16 +++--- .../bnd.bnd | 3 +- .../openems/backend/metadata/odoo/Odoo.java | 33 ++++++++---- .../impl/provider/UiWebsocket.java | 15 ++---- .../impl/provider/UiWebsocketServer.java | 51 ++++++++----------- .../common/websocket/DefaultMessages.java | 16 +----- 15 files changed, 154 insertions(+), 104 deletions(-) delete mode 100644 io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java create mode 100644 io.openems.backend.application/src/io/openems/backend/application/package-info.java create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java diff --git a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs index 5901b50bf09..bef3c56997f 100644 --- a/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.application/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,6 @@ eclipse.preferences.version=1 encoding//src/io/openems/backend/application/BackendApp.java=UTF-8 +encoding//src/io/openems/backend/application/package-info.java=UTF-8 encoding//test/io/openems/backend/application/BackendAppTest.java=UTF-8 encoding/BackendApp.bndrun=UTF-8 encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index a8c7e9658e8..c4abe1d04a7 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -9,6 +9,8 @@ import org.osgi.service.component.annotations.Component; 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.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,19 +24,19 @@ public class BackendApp { private final Logger log = LoggerFactory.getLogger(BackendApp.class); @Reference - ConfigurationAdmin configAdmin; + private ConfigurationAdmin configAdmin; @Reference - MetadataService metadataService; + private MetadataService metadataService; // @Reference // TimedataService timedataService; - @Reference - EdgeWebsocketService edgeWebsocketService; + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + private volatile EdgeWebsocketService edgeWebsocketService; @Reference - UiWebsocketService uiWebsocketService; + private UiWebsocketService uiWebsocketService; // @Reference(target = "(component.factory=EdgeWebsocketFactory)") // private ComponentFactory factory; diff --git a/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java b/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java deleted file mode 100644 index 03750cc92b1..00000000000 --- a/io.openems.backend.application/src/io/openems/backend/application/EdgeEventHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.openems.backend.application; - -import org.osgi.service.component.annotations.Component; -import org.osgi.service.event.Event; -import org.osgi.service.event.EventConstants; -import org.osgi.service.event.EventHandler; - -import io.openems.backend.common.events.BackendEventConstants; - -@Component(property = { // - EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_ONLINE, - EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_OFFLINE }) -public class EdgeEventHandler implements EventHandler { - - @Override - public void handleEvent(Event event) { - System.out.println(event); - } - -} diff --git a/io.openems.backend.application/src/io/openems/backend/application/package-info.java b/io.openems.backend.application/src/io/openems/backend/application/package-info.java new file mode 100644 index 00000000000..9fbcb56e542 --- /dev/null +++ b/io.openems.backend.application/src/io/openems/backend/application/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.application; diff --git a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java index d1c683192a5..c001d5ec8fe 100644 --- a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java +++ b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java @@ -4,4 +4,6 @@ @ProviderType public interface EdgeWebsocketService { + + boolean isOnline(int edgeId); } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 2a28e9032ed..c79e29b2bc5 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -7,6 +7,7 @@ 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.event.EventAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +30,7 @@ public class EdgeWebsocket implements EdgeWebsocketService { @Reference protected MetadataService metadataService; - @Reference(cardinality = ReferenceCardinality.OPTIONAL) // avoid recursive dependency + @Reference protected UiWebsocketService uiWebsocketService; @Reference @@ -77,4 +78,9 @@ private synchronized void startServer(int port) { this.server.start(); } + @Override + public boolean isOnline(int edgeId) { + return this.server.isOnline(edgeId); + } + } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 3cd51f2ca9b..e1c1291bdb9 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -189,4 +189,8 @@ protected void _onClose(WebSocket websocket) { } } } + + public boolean isOnline(int edgeId) { + return this.websocketsMap.containsKey(edgeId); + } } 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 25f7d68a241..9dc71574528 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 @@ -1,10 +1,13 @@ package io.openems.backend.metadata.api; +import com.google.gson.JsonObject; + public class Edge { private final int id; private String name; private String comment; private String producttype; + private boolean isOnline; public Edge(int id, String name, String comment, String producttype) { this.id = id; @@ -12,13 +15,34 @@ public Edge(int id, String name, String comment, String producttype) { this.comment = comment; this.producttype = producttype; } - + public int getId() { return id; } + /* + * Marks this Edge as being online. This is called by an event listener. + */ + public void setOnline(boolean isOnline) { + this.isOnline = isOnline; + } + + public boolean isOnline() { + return this.isOnline; + } + + public JsonObject toJsonObject() { + JsonObject j = new JsonObject(); + j.addProperty("name", this.name); + j.addProperty("comment", this.comment); + j.addProperty("producttype", this.producttype); + j.addProperty("online", this.isOnline); + return j; + } + @Override public String toString() { - return "Edge [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype + "]"; + return "Edge [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype + + ", isOnline=" + isOnline + "]"; } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java new file mode 100644 index 00000000000..b1dd01b1788 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java @@ -0,0 +1,47 @@ +package io.openems.backend.metadata.api; + +import java.util.Optional; + +import org.osgi.service.component.annotations.Component; +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.event.Event; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.backend.common.events.BackendEventConstants; + +@Component(property = { // + EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_ONLINE, + EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_OFFLINE }) +public class EdgeOnlineHandler implements EventHandler { + + private final Logger log = LoggerFactory.getLogger(EdgeOnlineHandler.class); + + @Reference + private MetadataService metadataService; + + @Override + public void handleEvent(Event event) { + log.info(event.toString()); + int edgeId = (int) event.getProperty("edgeId"); + Optional edgeOpt = this.metadataService.getEdge(edgeId); + if (edgeOpt.isPresent()) { + String topic = (String) event.getProperty("event.topics"); + if (topic.equals(BackendEventConstants.TOPIC_EDGE_ONLINE)) { + edgeOpt.get().setOnline(true); + log.debug("Marked Edge [ID:" + edgeId + "] as Online"); + } else if (topic.equals(BackendEventConstants.TOPIC_EDGE_OFFLINE)) { + edgeOpt.get().setOnline(false); + log.debug("Marked Edge [ID:" + edgeId + "] as Offline"); + } else { + log.warn("Unknown Topic: " + topic); + } + } else { + log.warn("Unable to get Edge [ID:" + edgeId + "]"); + } + } +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java index 2565dde154c..174a9464c8e 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java @@ -1,13 +1,13 @@ package io.openems.backend.metadata.api; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; public class User { private final int id; private String name; - private final Map deviceRoles = new HashMap<>(); + private final NavigableMap edgeRoles = new TreeMap<>(); public User(int id, String name) { this.id = id; @@ -22,16 +22,16 @@ public int getId() { return id; } - public void addDeviceRole(int deviceId, Role role) { - this.deviceRoles.put(deviceId, role); + public void addEdgeRole(int deviceId, Role role) { + this.edgeRoles.put(deviceId, role); } - public Map getDeviceRoles() { - return Collections.unmodifiableMap(this.deviceRoles); + public NavigableMap getEdgeRoles() { + return Collections.unmodifiableNavigableMap(this.edgeRoles); } @Override public String toString() { - return "User [id=" + id + ", name=" + name + ", deviceRole=" + deviceRoles + "]"; + return "User [id=" + id + ", name=" + name + ", edgeRole=" + edgeRoles + "]"; } } diff --git a/io.openems.backend.metadata.odoo.provider/bnd.bnd b/io.openems.backend.metadata.odoo.provider/bnd.bnd index 5d4534e14b3..11ec6274ea0 100644 --- a/io.openems.backend.metadata.odoo.provider/bnd.bnd +++ b/io.openems.backend.metadata.odoo.provider/bnd.bnd @@ -17,7 +17,8 @@ Private-Package: \ org.apache.servicemix.bundles.xmlrpc-client,\ com.google.gson,\ com.google.guava,\ - io.openems.backend.common;version=latest + io.openems.backend.common;version=latest,\ + io.openems.backend.edgewebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 44706cc261f..42cf9fb7b71 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -8,14 +8,21 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import javax.swing.plaf.synth.SynthSpinnerUI; + 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.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; @@ -26,6 +33,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.Edge; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.metadata.api.Role; @@ -42,11 +50,8 @@ public class Odoo implements MetadataService { @ObjectClassDefinition @interface Config { String database(); - String uid(); - String password(); - String url() default "https://www1.fenecon.de"; } @@ -55,9 +60,12 @@ public class Odoo implements MetadataService { private String uid; private String password; - private ConcurrentMap users = new ConcurrentHashMap<>(); - private ConcurrentMap edges = new ConcurrentHashMap<>(); + private Map users = new HashMap<>(); + private Map edges = new HashMap<>(); + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) // avoid recursive dependency + private volatile EdgeWebsocketService edgeWebsocketService; + @Activate void activate(Config config) { log.debug("Activate Odoo [url=" + config.url() + ";database=" + config.database() + ";uid=" + config.uid() @@ -126,10 +134,15 @@ public User getUserWithSession(String sessionId) throws OpenemsException { JsonUtils.getAsString(jDevice, "name"), // JsonUtils.getAsString(jDevice, "comment"), // JsonUtils.getAsString(jDevice, "producttype")); - this.edges.putIfAbsent(edge.getId(), edge); - user.addDeviceRole(edge.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); + edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); + synchronized (this.edges) { + this.edges.putIfAbsent(edge.getId(), edge); + } + user.addEdgeRole(edge.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); + } + synchronized (this.users) { + this.users.put(user.getId(), user); } - this.users.put(user.getId(), user); return user; } } @@ -151,9 +164,7 @@ public int[] getEdgeIdsForApikey(String apikey) { @Override public Optional getEdge(int edgeId) { - // TODO Auto-generated method stub - log.info("TODO: getEdge"); - return Optional.empty(); + return Optional.ofNullable(this.edges.get(edgeId)); } @Override diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 0edee91792a..5276623d394 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -7,6 +7,7 @@ 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.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,18 +29,10 @@ public class UiWebsocket implements UiWebsocketService { private UiWebsocketServer server = null; @Reference - private MetadataService metadataService; + protected MetadataService metadataService; - protected MetadataService getMetadataService() { - return metadataService; - } - - @Reference(cardinality = ReferenceCardinality.OPTIONAL) // avoid recursive dependency - private EdgeWebsocketService edgeWebsocketService; - - protected EdgeWebsocketService getEdgeWebsocketService() { - return edgeWebsocketService; - } + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) // avoid recursive dependency + protected volatile EdgeWebsocketService edgeWebsocketService; @ObjectClassDefinition @interface Config { diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 1dbd3df5fb9..58590ef8de8 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -12,10 +12,12 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import io.openems.backend.metadata.api.Edge; import io.openems.backend.metadata.api.Role; import io.openems.backend.metadata.api.User; import io.openems.common.exceptions.OpenemsException; import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.WebSocketUtils; public class UiWebsocketServer extends AbstractWebsocketServer { @@ -29,7 +31,6 @@ public UiWebsocketServer(UiWebsocket parent, int port) { @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - log.info("OnOpen"); String error = ""; User user = null; @@ -39,7 +40,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { error = "Session-ID is missing in handshake"; } else { try { - user = this.parent.getMetadataService().getUserWithSession(sessionIdOpt.get()); + user = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); } catch (OpenemsException e) { error = e.getMessage(); } @@ -50,39 +51,27 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { this.send(websocket, DefaultMessages.browserConnectionFailedReply()); log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + error + "]."); websocket.closeConnection(CloseFrame.REFUSE, error); + } else if (user != null) { // send connection successful to browser - for (Entry deviceRole : user.getDeviceRoles().entrySet()) { - // boolean isOnline = - // this.parent.getEdgeWebsocketService().isOnline(deviceRole.getKey()); - // JsonArray jDevices + JsonArray jEdges = new JsonArray(); + for (Entry edgeRole : user.getEdgeRoles().entrySet()) { + int edgeId = edgeRole.getKey(); + Role role = edgeRole.getValue(); + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (!edgeOpt.isPresent()) { + log.warn("Unable to find Edge [ID:" + edgeId + "]"); + } else { + JsonObject jEdge = edgeOpt.get().toJsonObject(); + jEdge.addProperty("role", role.toString()); + jEdges.add(jEdge); + } } - - // - // - // JsonObject jReply = - // DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), - // Optional.empty(), - // data.getDevices()); - // // TODO write user name to log output - // WebSocketUtils.send(websocket, jReply); - // - // // add websocket to local cache - // this.addWebsocket(websocket, session); - // log.info("User [" + data.getUserName() + "] connected with Session [" + - // data.getOdooSessionId().orElse("") - // + "]."); - + JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply("" /* TODO empty token? */, + Optional.empty(), jEdges); + WebSocketUtils.send(websocket, jReply); + log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); } - - // // check if the session is now valid and send reply to browser - // BrowserSessionData data = session.getData(); - // if (error.isEmpty()) { - // // add isOnline information - // - - // - } @Override diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 1e23d00a235..c2e12c4e42b 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -40,7 +40,7 @@ public class DefaultMessages { * @return */ public static JsonObject browserConnectionSuccessfulReply(String token, Optional roleOpt, - Collection devices) { + JsonArray jEdges) { JsonObject jAuthenticate = new JsonObject(); jAuthenticate.addProperty("mode", "allow"); if (roleOpt.isPresent()) { @@ -50,19 +50,7 @@ public static JsonObject browserConnectionSuccessfulReply(String token, Optional JsonObject j = new JsonObject(); j.add("authenticate", jAuthenticate); JsonObject jMetadata = new JsonObject(); - if (!devices.isEmpty()) { - JsonArray jDevices = new JsonArray(); - for (DeviceImpl device : devices) { - JsonObject jDevice = new JsonObject(); - jDevice.addProperty("name", device.getName()); - jDevice.addProperty("comment", device.getComment()); - jDevice.addProperty("producttype", device.getProducttype()); -// TODO jDevice.addProperty("role", device.getRole().toString()); - jDevice.addProperty("online", device.isOnline()); - jDevices.add(jDevice); - } - jMetadata.add("devices", jDevices); - } + jMetadata.add("devices", jEdges); j.add("metadata", jMetadata); return j; } From a35b29ee0abd75489927f24da8fa3bb3819e09ef Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Feb 2018 09:48:32 +0100 Subject: [PATCH 058/156] Implement search to Odoo + getEdgeIdsForApikey --- .../BackendApp.bndrun | 4 +- .../openems/backend/metadata/odoo/Domain.java | 13 ++++ .../openems/backend/metadata/odoo/Odoo.java | 27 ++++---- .../backend/metadata/odoo/OdooUtils.java | 65 +++++++++++++++++++ .../impl/provider/UiWebsocket.java | 4 +- 5 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Domain.java create mode 100644 io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 8aaadfb0251..c72bd40731f 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -36,4 +36,6 @@ JPM-Command: openems-backend io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ org.osgi.service.event;version='[1.3.1,1.3.2)',\ - io.openems.backend.uiwebsocket.impl.provider;version=snapshot \ No newline at end of file + io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ + org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ + org.apache.servicemix.bundles.xmlrpc-client;version='[3.1.3,3.1.4)' \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Domain.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Domain.java new file mode 100644 index 00000000000..89d74090c8c --- /dev/null +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Domain.java @@ -0,0 +1,13 @@ +package io.openems.backend.metadata.odoo; + +public class Domain { + protected final String field; + protected final String operator; + protected final Object value; + + public Domain(String field, String operator, Object value) { + this.field = field; + this.operator = operator; + this.value = value; + } +} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 42cf9fb7b71..e45d36b502b 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -11,10 +11,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.swing.plaf.synth.SynthSpinnerUI; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -50,22 +46,25 @@ public class Odoo implements MetadataService { @ObjectClassDefinition @interface Config { String database(); - String uid(); + + int uid(); + String password(); + String url() default "https://www1.fenecon.de"; } private String url; private String database; - private String uid; + private int uid; private String password; private Map users = new HashMap<>(); private Map edges = new HashMap<>(); - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) // avoid recursive dependency + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) private volatile EdgeWebsocketService edgeWebsocketService; - + @Activate void activate(Config config) { log.debug("Activate Odoo [url=" + config.url() + ";database=" + config.database() + ";uid=" + config.uid() @@ -136,12 +135,12 @@ public User getUserWithSession(String sessionId) throws OpenemsException { JsonUtils.getAsString(jDevice, "producttype")); edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); synchronized (this.edges) { - this.edges.putIfAbsent(edge.getId(), edge); + this.edges.putIfAbsent(edge.getId(), edge); } user.addEdgeRole(edge.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); } synchronized (this.users) { - this.users.put(user.getId(), user); + this.users.put(user.getId(), user); } return user; } @@ -158,8 +157,12 @@ public User getUserWithSession(String sessionId) throws OpenemsException { @Override public int[] getEdgeIdsForApikey(String apikey) { - // TODO Auto-generated method stub - return new int[] { -1 }; + try { + return OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", new Domain("apikey", "=", apikey)); + } catch (OpenemsException e) { + log.error("Unable to get EdgeIds for Apikey: " + e.getMessage()); + return new int[] {}; + } } @Override diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java new file mode 100644 index 00000000000..ae0de2438e2 --- /dev/null +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -0,0 +1,65 @@ +package io.openems.backend.metadata.odoo; + +import java.net.URL; +import java.util.HashMap; + +import org.apache.xmlrpc.client.XmlRpcClient; +import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; + +import io.openems.common.exceptions.OpenemsException; + +public class OdooUtils { + + private OdooUtils() { + } + + /** + * Executes a search on Odoo + * + * @param url + * URL of Odoo instance + * @param database + * Database name + * @param uid + * UID of user (e.g. '1' for admin) + * @param password + * Password of user + * @param model + * Odoo model to query (e.g. 'res.partner') + * @param domains + * Odoo domain filters + * @return Odoo object ids + * @throws OpenemsException + */ + protected static int[] search(String url, String database, int uid, String password, String model, + Domain... domains) throws OpenemsException { + final XmlRpcClient client = new XmlRpcClient(); + try { + // Build XML request to Odoo + XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); + config.setEnabledForExtensions(true); + config.setServerURL(new URL(String.format("%s/xmlrpc/2/object", url))); + client.setConfig(config); + // Add domain filter + Object[] domain = new Object[domains.length]; + for (int i = 0; i < domains.length; i++) { + Domain filter = domains[i]; + domain[i] = new Object[] { filter.field, filter.operator, filter.value }; + } + Object[] paramsDomain = new Object[] { domain }; + HashMap paramsRules = new HashMap(); + String action = "search"; + Object[] params = new Object[] { database, uid, password, model, action, paramsDomain, paramsRules }; + // Execute XML request + Object[] resultObjs = (Object[]) client.execute("execute_kw", params); + // Parse results + int[] results = new int[resultObjs.length]; + for (int i = 0; i < resultObjs.length; i++) { + results[i] = (int) resultObjs[i]; + } + return results; + } catch (Throwable e) { + throw new OpenemsException("Unable to search from Odoo: " + e.getMessage()); + } + } +} diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 5276623d394..1a70f9c1fa8 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -31,8 +31,8 @@ public class UiWebsocket implements UiWebsocketService { @Reference protected MetadataService metadataService; - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) // avoid recursive dependency - protected volatile EdgeWebsocketService edgeWebsocketService; + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + private volatile EdgeWebsocketService edgeWebsocketService; @ObjectClassDefinition @interface Config { From c9c737b7f04fdab58886c0ea0cadebb7c72530bc Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Feb 2018 11:29:57 +0100 Subject: [PATCH 059/156] Implement Odoo read --- .../backend/metadata/odoo/OdooUtils.java | 149 ++++++++++++++++-- 1 file changed, 132 insertions(+), 17 deletions(-) diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java index ae0de2438e2..d37abb69dab 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -1,8 +1,13 @@ package io.openems.backend.metadata.odoo; +import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; @@ -13,6 +18,15 @@ public class OdooUtils { private OdooUtils() { } + private static Object[] executeKw(String url, Object[] params) throws XmlRpcException, MalformedURLException { + final XmlRpcClient client = new XmlRpcClient(); + XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); + config.setEnabledForExtensions(true); + config.setServerURL(new URL(String.format("%s/xmlrpc/2/object", url))); + client.setConfig(config); + return (Object[]) client.execute("execute_kw", params); + } + /** * Executes a search on Odoo * @@ -33,25 +47,20 @@ private OdooUtils() { */ protected static int[] search(String url, String database, int uid, String password, String model, Domain... domains) throws OpenemsException { - final XmlRpcClient client = new XmlRpcClient(); + // Add domain filter + Object[] domain = new Object[domains.length]; + for (int i = 0; i < domains.length; i++) { + Domain filter = domains[i]; + domain[i] = new Object[] { filter.field, filter.operator, filter.value }; + } + Object[] paramsDomain = new Object[] { domain }; + // Create request params + HashMap paramsRules = new HashMap(); + String action = "search"; + Object[] params = new Object[] { database, uid, password, model, action, paramsDomain, paramsRules }; try { - // Build XML request to Odoo - XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); - config.setEnabledForExtensions(true); - config.setServerURL(new URL(String.format("%s/xmlrpc/2/object", url))); - client.setConfig(config); - // Add domain filter - Object[] domain = new Object[domains.length]; - for (int i = 0; i < domains.length; i++) { - Domain filter = domains[i]; - domain[i] = new Object[] { filter.field, filter.operator, filter.value }; - } - Object[] paramsDomain = new Object[] { domain }; - HashMap paramsRules = new HashMap(); - String action = "search"; - Object[] params = new Object[] { database, uid, password, model, action, paramsDomain, paramsRules }; // Execute XML request - Object[] resultObjs = (Object[]) client.execute("execute_kw", params); + Object[] resultObjs = executeKw(url, params); // Parse results int[] results = new int[resultObjs.length]; for (int i = 0; i < resultObjs.length; i++) { @@ -62,4 +71,110 @@ protected static int[] search(String url, String database, int uid, String passw throw new OpenemsException("Unable to search from Odoo: " + e.getMessage()); } } + + /** + * Reads a record from Odoo + * + * @param url + * URL of Odoo instance + * @param database + * Database name + * @param uid + * UID of user (e.g. '1' for admin) + * @param password + * Password of user + * @param model + * Odoo model to query (e.g. 'res.partner') + * @param ids + * ids of model to read + * @param fields + * fields that should be read + * @return + * @throws OpenemsException + */ + protected static Map readOne(String url, String database, int uid, String password, String model, + int id, Fields... fields) throws OpenemsException { + // Create request params + String action = "read"; + // Add ids + Object[] paramsIds = new Object[1]; + paramsIds[0] = id; + // Add fields + String[] fieldStrings = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + fieldStrings[i] = fields[i].n(); + } + Map paramsFields = new HashMap<>(); + paramsFields.put("fields", fieldStrings); + // Create request params + Object[] params = new Object[] { database, uid, password, model, action, paramsIds, paramsFields }; + try { + // Execute XML request + Object[] resultObjs = executeKw(url, params); + // Parse results + for (int i = 0; i < resultObjs.length;) { + @SuppressWarnings("unchecked") + Map result = (Map) resultObjs[i]; + return result; + } + throw new OpenemsException("No matching entry found for id [" + id + "]"); + } catch (Throwable e) { + throw new OpenemsException("Unable to search from Odoo: " + e.getMessage()); + } + } + + /** + * Reads multiple records from Odoo + * + * @param url + * URL of Odoo instance + * @param database + * Database name + * @param uid + * UID of user (e.g. '1' for admin) + * @param password + * Password of user + * @param model + * Odoo model to query (e.g. 'res.partner') + * @param ids + * ids of model to read + * @param fields + * fields that should be read + * @return + * @throws OpenemsException + */ + protected static Map[] readMany(String url, String database, int uid, String password, String model, + int[] ids, Fields... fields) throws OpenemsException { + // Create request params + String action = "read"; + // Add ids + Object[] paramsIds = new Object[ids.length]; + for (int i = 0; i < ids.length; i++) { + paramsIds[i] = ids[i]; + } + // Add fields + String[] fieldStrings = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + fieldStrings[i] = fields[i].toString(); + } + Map paramsFields = new HashMap<>(); + paramsFields.put("fields", fieldStrings); + // Create request params + Object[] params = new Object[] { database, uid, password, model, action, paramsIds, paramsFields }; + try { + // Execute XML request + Object[] resultObjs = executeKw(url, params); + // Parse results + @SuppressWarnings("unchecked") + Map[] results = (Map[]) new Map[resultObjs.length]; + for (int i = 0; i < resultObjs.length; i++) { + @SuppressWarnings("unchecked") + Map result = (Map) resultObjs[i]; + results[0] = result; + } + return results; + } catch (Throwable e) { + throw new OpenemsException("Unable to search from Odoo: " + e.getMessage()); + } + } } From 2db341eb58c9d3eda6ec91f752e384e5b8fdbf5e Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Feb 2018 11:30:21 +0100 Subject: [PATCH 060/156] Implement Odoo.getEdge() --- .../impl/provider/EdgeWebsocketServer.java | 6 ++--- .../io/openems/backend/metadata/api/Edge.java | 4 ++++ .../metadata/api/EdgeOnlineHandler.java | 7 +++--- .../openems/backend/metadata/odoo/Fields.java | 19 +++++++++++++++ .../openems/backend/metadata/odoo/Odoo.java | 23 +++++++++++++++++-- 5 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index e1c1291bdb9..4abfc6d0d54 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -79,9 +79,9 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { // log for (int edgeId : edgeIds) { - Optional deviceOpt = this.parent.metadataService.getEdge(edgeId); - if (deviceOpt.isPresent()) { - log.info("Device [" + deviceOpt.get() + "] connected."); + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (edgeOpt.isPresent()) { + log.info("Device [" + edgeOpt.get().getName() + "] connected."); } else { log.info("Device [ID:" + edgeId + "] connected."); } 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 9dc71574528..84c288c87e7 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 @@ -20,6 +20,10 @@ public int getId() { return id; } + public String getName() { + return name; + } + /* * Marks this Edge as being online. This is called by an event listener. */ diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java index b1dd01b1788..230db909cf3 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java @@ -4,8 +4,6 @@ import org.osgi.service.component.annotations.Component; 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.event.Event; import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; @@ -30,13 +28,14 @@ public void handleEvent(Event event) { int edgeId = (int) event.getProperty("edgeId"); Optional edgeOpt = this.metadataService.getEdge(edgeId); if (edgeOpt.isPresent()) { + Edge edge = edgeOpt.get(); String topic = (String) event.getProperty("event.topics"); if (topic.equals(BackendEventConstants.TOPIC_EDGE_ONLINE)) { edgeOpt.get().setOnline(true); - log.debug("Marked Edge [ID:" + edgeId + "] as Online"); + log.debug("Marked Edge [" + edge.getName() + "] as Online"); } else if (topic.equals(BackendEventConstants.TOPIC_EDGE_OFFLINE)) { edgeOpt.get().setOnline(false); - log.debug("Marked Edge [ID:" + edgeId + "] as Offline"); + log.debug("Marked Edge [" + edge.getName() + "] as Offline"); } else { log.warn("Unknown Topic: " + topic); } diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java new file mode 100644 index 00000000000..842db103833 --- /dev/null +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java @@ -0,0 +1,19 @@ +package io.openems.backend.metadata.odoo; + +public interface Fields { + public String n(); + + public enum FemsDevice implements Fields { + ID("id"), NAME("name"), COMMENT("comment"), PRODUCT_TYPE("producttype"); + + private final String n; + + private FemsDevice(String n) { + this.n = n; + } + + public String n() { + return n; + } + } +} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index e45d36b502b..3acc0fe8583 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -158,7 +158,8 @@ public User getUserWithSession(String sessionId) throws OpenemsException { @Override public int[] getEdgeIdsForApikey(String apikey) { try { - return OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", new Domain("apikey", "=", apikey)); + return OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", + new Domain("apikey", "=", apikey)); } catch (OpenemsException e) { log.error("Unable to get EdgeIds for Apikey: " + e.getMessage()); return new int[] {}; @@ -167,7 +168,25 @@ public int[] getEdgeIdsForApikey(String apikey) { @Override public Optional getEdge(int edgeId) { - return Optional.ofNullable(this.edges.get(edgeId)); + try { + // TODO: read from cache + // return Optional.ofNullable(this.edges.get(edgeId)); + Map edgeMap = OdooUtils.readOne(this.url, this.database, this.uid, this.password, + "fems.device", edgeId, Fields.FemsDevice.NAME, Fields.FemsDevice.COMMENT, + Fields.FemsDevice.PRODUCT_TYPE); + Edge edge = new Edge( // + (Integer) edgeMap.get(Fields.FemsDevice.ID.n()), // + (String) edgeMap.get(Fields.FemsDevice.NAME.n()), // + (String) edgeMap.get(Fields.FemsDevice.COMMENT.n()), // + (String) edgeMap.get(Fields.FemsDevice.PRODUCT_TYPE.n())); + synchronized (this.edges) { + this.edges.put(edge.getId(), edge); + } + return Optional.ofNullable(edge); + } catch (OpenemsException e) { + log.error("Unable to read Edge for id [" + edgeId + "]: " + e.getMessage()); + return Optional.empty(); + } } @Override From 2eb4c6230f46b5f267503aae24b869fe40e5ad23 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Feb 2018 16:34:55 +0100 Subject: [PATCH 061/156] Improve logs; use cache for Edges; handle Odoo timeout --- .../edgewebsocket/impl/provider/EdgeWebsocket.java | 8 ++++---- .../impl/provider/EdgeWebsocketServer.java | 6 +++--- .../backend/metadata/api/EdgeOnlineHandler.java | 1 - .../src/io/openems/backend/metadata/odoo/Odoo.java | 10 ++++++++-- .../io/openems/backend/metadata/odoo/OdooUtils.java | 3 +-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index c79e29b2bc5..ec65080523d 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -27,11 +27,11 @@ public class EdgeWebsocket implements EdgeWebsocketService { private EdgeWebsocketServer server = null; - @Reference - protected MetadataService metadataService; + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected volatile MetadataService metadataService; - @Reference - protected UiWebsocketService uiWebsocketService; + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected volatile UiWebsocketService uiWebsocketService; @Reference protected EventAdmin eventAdmin; diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 4abfc6d0d54..f09bf2a7ace 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -181,9 +181,9 @@ protected void _onClose(WebSocket websocket) { // log for (int edgeId : edgeIds) { - Optional deviceOpt = this.parent.metadataService.getEdge(edgeId); - if (deviceOpt.isPresent()) { - log.info("Device [" + deviceOpt.get() + "] disconnected."); + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (edgeOpt.isPresent()) { + log.info("Device [" + edgeOpt.get().getName() + "] disconnected."); } else { log.info("Device [ID:" + edgeId + "] disconnected."); } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java index 230db909cf3..d2d464c040a 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java @@ -24,7 +24,6 @@ public class EdgeOnlineHandler implements EventHandler { @Override public void handleEvent(Event event) { - log.info(event.toString()); int edgeId = (int) event.getProperty("edgeId"); Optional edgeOpt = this.metadataService.getEdge(edgeId); if (edgeOpt.isPresent()) { diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 3acc0fe8583..6329628e70f 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -168,9 +168,14 @@ public int[] getEdgeIdsForApikey(String apikey) { @Override public Optional getEdge(int edgeId) { + // try to read from cache + synchronized (this.edges) { + if (this.edges.containsKey(edgeId)) { + return Optional.of(this.edges.get(edgeId)); + } + } + // if it was not in cache: try { - // TODO: read from cache - // return Optional.ofNullable(this.edges.get(edgeId)); Map edgeMap = OdooUtils.readOne(this.url, this.database, this.uid, this.password, "fems.device", edgeId, Fields.FemsDevice.NAME, Fields.FemsDevice.COMMENT, Fields.FemsDevice.PRODUCT_TYPE); @@ -179,6 +184,7 @@ public Optional getEdge(int edgeId) { (String) edgeMap.get(Fields.FemsDevice.NAME.n()), // (String) edgeMap.get(Fields.FemsDevice.COMMENT.n()), // (String) edgeMap.get(Fields.FemsDevice.PRODUCT_TYPE.n())); + edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); synchronized (this.edges) { this.edges.put(edge.getId(), edge); } diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java index d37abb69dab..2d652fa4d25 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -2,9 +2,7 @@ import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.xmlrpc.XmlRpcException; @@ -23,6 +21,7 @@ private static Object[] executeKw(String url, Object[] params) throws XmlRpcExce XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setEnabledForExtensions(true); config.setServerURL(new URL(String.format("%s/xmlrpc/2/object", url))); + config.setReplyTimeout(5000); client.setConfig(config); return (Object[]) client.execute("execute_kw", params); } From f5ae1f1dae6d8c493eb10364f7befa06157833bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Thu, 15 Feb 2018 10:26:04 +0100 Subject: [PATCH 062/156] add Bridge as Parameter for power --- .../utilities/power/SymmetricPowerImpl.java | 12 +++++++++--- .../io/openems/core/utilities/power/Test.java | 4 ++-- .../impl/device/byd/Bem125ktla01Ess.java | 2 +- .../device/commercial/FeneconCommercialEss.java | 17 +++++++++++------ .../impl/device/mini/FeneconMiniEss.java | 2 +- .../io/openems/impl/device/refu/RefuEss.java | 2 +- .../device/simulator/SimulatorSymmetricEss.java | 7 ++++--- .../impl/device/sma/SunnyIsland6Ess.java | 2 +- .../UnitTestSymmetricEssNature.java | 2 +- 9 files changed, 31 insertions(+), 19 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java index e1970207638..1ae62f489cc 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java @@ -7,11 +7,12 @@ import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Point; +import io.openems.api.bridge.Bridge; import io.openems.api.bridge.BridgeEvent; import io.openems.api.bridge.BridgeEventListener; import io.openems.api.channel.WriteChannel; import io.openems.api.exception.WriteChannelException; -import io.openems.core.utilities.AvgFiFoQueue;; +import io.openems.core.utilities.AvgFiFoQueue; public class SymmetricPowerImpl extends SymmetricPower implements LimitationChangedListener, BridgeEventListener { /* @@ -28,14 +29,19 @@ public class SymmetricPowerImpl extends SymmetricPower implements LimitationChan private AvgFiFoQueue reactivePowerAvg; public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePower, - WriteChannel setReactivePower) { + WriteChannel setReactivePower, Bridge bridge) { setMaxApparentPower(maxApparentPower); this.staticLimitations = new ArrayList<>(); this.dynamicLimitations = new ArrayList<>(); this.setActivePower = setActivePower; this.setReactivePower = setReactivePower; activePowerAvg = new AvgFiFoQueue(3, 1.5); - reactivePowerAvg = new AvgFiFoQueue(3, 1.5); + reactivePowerAvg = new AvgFiFoQueue(1, 1.5); + if(bridge != null) { + bridge.addListener(this); + }else { + log.error("the Bridge is null! the Power Values won't be writte!"); + } createBaseGeometry(); reset(); } diff --git a/edge/src/io/openems/core/utilities/power/Test.java b/edge/src/io/openems/core/utilities/power/Test.java index 59ae65dd168..27e7183a899 100644 --- a/edge/src/io/openems/core/utilities/power/Test.java +++ b/edge/src/io/openems/core/utilities/power/Test.java @@ -15,8 +15,8 @@ public class Test { public static void main(String[] args) throws PowerException { Stopwatch sw = Stopwatch.createStarted(); - SymmetricPowerImpl power = new SymmetricPowerImpl(100000, null, null); - SymmetricPowerImpl power2 = new SymmetricPowerImpl(100000, null, null); + SymmetricPowerImpl power = new SymmetricPowerImpl(100000, null, null, null); + SymmetricPowerImpl power2 = new SymmetricPowerImpl(100000, null, null, null); System.out.println(sw.elapsed().toMillis()); sw.reset(); sw.start(); diff --git a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java index db83fc51f33..14a6af32eb1 100644 --- a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java +++ b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java @@ -240,7 +240,7 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new UnsignedDoublewordElement(0x130C, // batteryStackTotalDischarge = new ModbusReadLongChannel( "BatteryStackTotalDischarge", this).unit("kWh")))); - this.power = new SymmetricPowerImpl(125000, setActivePower, setReactivePower); + this.power = new SymmetricPowerImpl(125000, setActivePower, setReactivePower, getParent().getBridge()); return protocol; } diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java index 1b431df6e0c..34e45b278fe 100644 --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java @@ -33,9 +33,10 @@ import io.openems.api.device.nature.ess.SymmetricEssNature; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; -import io.openems.core.utilities.power.MaxCosPhiLimitation; import io.openems.core.utilities.power.PGreaterEqualLimitation; import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.QGreaterEqualLimitation; +import io.openems.core.utilities.power.QSmallerEqualLimitation; import io.openems.core.utilities.power.SMaxLimitation; import io.openems.core.utilities.power.SymmetricPowerImpl; import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel; @@ -104,7 +105,8 @@ public ConfigChannel chargeSoc() { .unit("VA"); private StaticValueChannel capacity = new StaticValueChannel<>("capacity", this, 40000L).unit("Wh"); private SymmetricPowerImpl power; - private MaxCosPhiLimitation cosPhiLimit; + private QGreaterEqualLimitation qMinLimit; + private QSmallerEqualLimitation qMaxLimit; private PGreaterEqualLimitation allowedChargeLimit; private PSmallerEqualLimitation allowedDischargeLimit; private SMaxLimitation allowedApparentLimit; @@ -1447,10 +1449,13 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new UnsignedWordElement(0x15DF, batteryCell224Voltage = new ModbusReadLongChannel("Cell224Voltage", this).unit("mV") ))); - this.power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower); - this.cosPhiLimit = new MaxCosPhiLimitation(power); - this.cosPhiLimit.setMaxCosPhi(0.8); - this.power.addStaticLimitation(cosPhiLimit); + this.power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower, getParent().getBridge()); + this.qMinLimit = new QGreaterEqualLimitation(power); + this.qMinLimit.setQ(-10000L); + this.power.addStaticLimitation(qMinLimit); + this.qMaxLimit = new QSmallerEqualLimitation(power); + this.qMaxLimit.setQ(10000L); + this.power.addStaticLimitation(qMaxLimit); this.allowedApparentLimit = new SMaxLimitation(power); this.allowedApparentLimit.setSMax(allowedApparent.valueOptional().orElse(0L), 0L, 0L); this.allowedApparent.addChangeListener(new ChannelChangeListener() { diff --git a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java index 3cd83d85709..9e788764b99 100644 --- a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java +++ b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java @@ -443,7 +443,7 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { } return 0l; }, activePower, reactivePower); - this.power = new SymmetricPowerImpl(3000, setActivePower, setReactivePower); + this.power = new SymmetricPowerImpl(3000, setActivePower, setReactivePower,getParent().getBridge()); this.allowedApparentLimit = new SMaxLimitation(power); this.allowedApparentLimit.setSMax(phaseAllowedApparent.valueOptional().orElse(0L)*3, 0L, 0L); this.phaseAllowedApparent.addChangeListener(new ChannelChangeListener() { diff --git a/edge/src/io/openems/impl/device/refu/RefuEss.java b/edge/src/io/openems/impl/device/refu/RefuEss.java index af6e0f4f797..f11475b2b8e 100644 --- a/edge/src/io/openems/impl/device/refu/RefuEss.java +++ b/edge/src/io/openems/impl/device/refu/RefuEss.java @@ -659,7 +659,7 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new SignedWordElement(0x20A, // setReactivePowerL3 = new ModbusWriteLongChannel("SetReactivePowerL3", this)// .unit("W").multiplier(2)))); - this.power = new SymmetricPowerImpl(100000, setActivePower, setReactivePower); + this.power = new SymmetricPowerImpl(100000, setActivePower, setReactivePower, getParent().getBridge()); this.allowedChargeLimit = new PGreaterEqualLimitation(power); this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); this.batFullLimit = new NoPBetweenLimitation(power); diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index cc9edf89112..b6fc3993a94 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -119,7 +119,7 @@ public SimulatorSymmetricEss(String thingId, Device parent) throws ConfigExcepti } return 0L; }, this.activePower); - power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower); + power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower, getParent().getBridge()); this.allowedChargeLimit = new PGreaterEqualLimitation(power); this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); this.allowedCharge.addChangeListener(new ChannelChangeListener() { @@ -140,7 +140,6 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } }); this.power.addStaticLimitation(this.allowedDischargeLimit); - getParent().getBridge().addListener(this.power); } /* @@ -274,9 +273,11 @@ protected void update() { reactivePower = reactivePowerQueue.avg(); } } + long apparentPower = ControllerUtils.calculateApparentPower(activePower, reactivePower); + System.out.println(id()+" [CosPhi: "+(double)activePower / (double)apparentPower+"]"); this.activePower.updateValue(activePower); this.reactivePower.updateValue(reactivePower); - this.apparentPower.updateValue(ControllerUtils.calculateApparentPower(activePower, reactivePower)); + this.apparentPower.updateValue(apparentPower); this.allowedCharge.updateValue(-9000L); this.allowedDischarge.updateValue(3000L); } diff --git a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java index 1af967452a5..4515df6346c 100644 --- a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java +++ b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java @@ -195,7 +195,7 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { new UnsignedDoublewordElement(41187, meterSetting = new ModbusWriteLongChannel("MeterSetting", this) .label(3053, "SMA Energy Meter").label(3547, "Wechselrichter")))); - this.power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower); + this.power = new SymmetricPowerImpl(40000, setActivePower, setReactivePower, getParent().getBridge()); this.allowedChargeLimit = new PGreaterEqualLimitation(power); this.allowedChargeLimit.setP(this.allowedCharge.valueOptional().orElse(0L)); this.allowedCharge.addChangeListener(new ChannelChangeListener() { diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java index d5178db92dd..12ead678c04 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java @@ -39,7 +39,7 @@ public class UnitTestSymmetricEssNature implements SymmetricEssNature { public UnitTestWriteChannel setReactivePower = new UnitTestWriteChannel<>("SetReactivePower", this); public StaticValueChannel capacity = new StaticValueChannel("Capacity", this, SimulatorTools.getRandomLong(3000, 50000)); - public SymmetricPowerImpl power = new SymmetricPowerImpl(9000, setActivePower, setReactivePower); + public SymmetricPowerImpl power = new SymmetricPowerImpl(9000, setActivePower, setReactivePower, getParent().getBridge()); private final String id; private ThingStateChannel thingState; From b40889601e93ae581c4d502d6cd11d02835b302d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Thu, 15 Feb 2018 10:27:22 +0100 Subject: [PATCH 063/156] cosPhi position calculation --- .../balancingcosphi/BalancingCosPhiController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java index ff4412b7438..0961b4f8ecb 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java @@ -67,9 +67,9 @@ public void run() { Ess ess = this.ess.value(); Meter meter = this.meter.value(); //Calculate the startpoint of the cosPhi line in relation to the ess zero power - long pNull = (meter.activePower.value()-ess.activePower.value())*-1; - long qNull = (meter.reactivePower.value()-ess.reactivePower.value())*-1; - ess.limit.setCosPhi(cosPhi.value(), capacitive.value(), pNull, qNull); + long pNull = meter.activePower.value()+ess.activePower.value(); + long qNull = meter.reactivePower.value()+ess.reactivePower.value(); + ess.limit.setCosPhi(cosPhi.value(), !capacitive.value(), pNull, qNull); ess.power.applyLimitation(ess.limit); } catch (InvalidValueException e) { log.error("Failed to read value.", e); From e5618d69876c5b2c6946ab19a949fc48023ca8d8 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 15 Feb 2018 20:28:58 +0100 Subject: [PATCH 064/156] Handle cache of Edge objects --- .../backend/metadata/api/MetadataService.java | 2 +- .../openems/backend/metadata/odoo/Odoo.java | 35 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 810dba79b32..6d776035384 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -16,7 +16,7 @@ public interface MetadataService { public abstract int[] getEdgeIdsForApikey(String apikey); public abstract Optional getEdge(int edgeId); - + public abstract void updateEdgeConfig(int edgeId, JsonObject jConfig); } diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 6329628e70f..b8055c16301 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -128,16 +128,15 @@ public User getUserWithSession(String sessionId) throws OpenemsException { JsonUtils.getAsString(jUser, "name")); JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices"); for (JsonElement jDevice : jDevices) { - Edge edge = new Edge(// - JsonUtils.getAsInt(jDevice, "id"), // - JsonUtils.getAsString(jDevice, "name"), // - JsonUtils.getAsString(jDevice, "comment"), // - JsonUtils.getAsString(jDevice, "producttype")); - edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); - synchronized (this.edges) { - this.edges.putIfAbsent(edge.getId(), edge); + int edgeId = JsonUtils.getAsInt(jDevice, "id"); + Optional edgeOpt = this.getEdge(edgeId); + if (edgeOpt.isPresent()) { + Edge edge = edgeOpt.get(); + synchronized (this.edges) { + this.edges.putIfAbsent(edge.getId(), edge); + } } - user.addEdgeRole(edge.getId(), Role.getRole(JsonUtils.getAsString(jDevice, "role"))); + user.addEdgeRole(edgeId, Role.getRole(JsonUtils.getAsString(jDevice, "role"))); } synchronized (this.users) { this.users.put(user.getId(), user); @@ -158,8 +157,13 @@ public User getUserWithSession(String sessionId) throws OpenemsException { @Override public int[] getEdgeIdsForApikey(String apikey) { try { - return OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", + int[] edgeIds = OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", new Domain("apikey", "=", apikey)); + // refresh Edge cache + for(int edgeId : edgeIds) { + this.getEdgeForceRefresh(edgeId); + } + return edgeIds; } catch (OpenemsException e) { log.error("Unable to get EdgeIds for Apikey: " + e.getMessage()); return new int[] {}; @@ -175,6 +179,16 @@ public Optional getEdge(int edgeId) { } } // if it was not in cache: + return this.getEdgeForceRefresh(edgeId); + } + + /** + * Reads the Edge object from Odoo and stores it in the cache + * + * @param edgeId + * @return + */ + private Optional getEdgeForceRefresh(int edgeId) { try { Map edgeMap = OdooUtils.readOne(this.url, this.database, this.uid, this.password, "fems.device", edgeId, Fields.FemsDevice.NAME, Fields.FemsDevice.COMMENT, @@ -185,6 +199,7 @@ public Optional getEdge(int edgeId) { (String) edgeMap.get(Fields.FemsDevice.COMMENT.n()), // (String) edgeMap.get(Fields.FemsDevice.PRODUCT_TYPE.n())); edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); + // store in cache synchronized (this.edges) { this.edges.put(edge.getId(), edge); } From ba1653dd6e0db30e5e8221a87ae0f25945af0a7c Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 15 Feb 2018 21:45:52 +0100 Subject: [PATCH 065/156] Write OpenemsConfig to Odoo --- .../metadata/odoo/{Fields.java => Field.java} | 6 +- .../backend/metadata/odoo/FieldValue.java | 19 ++++++ .../openems/backend/metadata/odoo/Odoo.java | 28 +++++--- .../backend/metadata/odoo/OdooUtils.java | 64 ++++++++++++++++--- 4 files changed, 95 insertions(+), 22 deletions(-) rename io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/{Fields.java => Field.java} (69%) create mode 100644 io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/FieldValue.java diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java similarity index 69% rename from io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java rename to io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java index 842db103833..2cc7692d16f 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Fields.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java @@ -1,10 +1,10 @@ package io.openems.backend.metadata.odoo; -public interface Fields { +public interface Field { public String n(); - public enum FemsDevice implements Fields { - ID("id"), NAME("name"), COMMENT("comment"), PRODUCT_TYPE("producttype"); + public enum FemsDevice implements Field { + ID("id"), NAME("name"), COMMENT("comment"), PRODUCT_TYPE("producttype"), OPENEMS_CONFIG("openems_config"); private final String n; diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/FieldValue.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/FieldValue.java new file mode 100644 index 00000000000..2ec6371576f --- /dev/null +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/FieldValue.java @@ -0,0 +1,19 @@ +package io.openems.backend.metadata.odoo; + +public class FieldValue { + private final Field field; + private final String value; + + public FieldValue(Field field, String value) { + this.field = field; + this.value = value; + } + + public Field getField() { + return field; + } + + public String getValue() { + return value; + } +} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index b8055c16301..55d17b634cb 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -157,10 +158,11 @@ public User getUserWithSession(String sessionId) throws OpenemsException { @Override public int[] getEdgeIdsForApikey(String apikey) { try { + // TODO use Odoo "search_read" method int[] edgeIds = OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", new Domain("apikey", "=", apikey)); // refresh Edge cache - for(int edgeId : edgeIds) { + for (int edgeId : edgeIds) { this.getEdgeForceRefresh(edgeId); } return edgeIds; @@ -191,13 +193,13 @@ public Optional getEdge(int edgeId) { private Optional getEdgeForceRefresh(int edgeId) { try { Map edgeMap = OdooUtils.readOne(this.url, this.database, this.uid, this.password, - "fems.device", edgeId, Fields.FemsDevice.NAME, Fields.FemsDevice.COMMENT, - Fields.FemsDevice.PRODUCT_TYPE); + "fems.device", edgeId, Field.FemsDevice.NAME, Field.FemsDevice.COMMENT, + Field.FemsDevice.PRODUCT_TYPE); Edge edge = new Edge( // - (Integer) edgeMap.get(Fields.FemsDevice.ID.n()), // - (String) edgeMap.get(Fields.FemsDevice.NAME.n()), // - (String) edgeMap.get(Fields.FemsDevice.COMMENT.n()), // - (String) edgeMap.get(Fields.FemsDevice.PRODUCT_TYPE.n())); + (Integer) edgeMap.get(Field.FemsDevice.ID.n()), // + (String) edgeMap.get(Field.FemsDevice.NAME.n()), // + (String) edgeMap.get(Field.FemsDevice.COMMENT.n()), // + (String) edgeMap.get(Field.FemsDevice.PRODUCT_TYPE.n())); edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); // store in cache synchronized (this.edges) { @@ -205,15 +207,21 @@ private Optional getEdgeForceRefresh(int edgeId) { } return Optional.ofNullable(edge); } catch (OpenemsException e) { - log.error("Unable to read Edge for id [" + edgeId + "]: " + e.getMessage()); + log.error("Unable to read Edge [ID:" + edgeId + "]: " + e.getMessage()); return Optional.empty(); } } @Override public void updateEdgeConfig(int edgeId, JsonObject jConfig) { - // TODO Auto-generated method stub - log.info("TODO: updateEdgeConfig"); + try { + String config = new GsonBuilder().setPrettyPrinting().create().toJson(jConfig); + OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edgeId, + new FieldValue(Field.FemsDevice.OPENEMS_CONFIG, config)); + log.info("Updated Edge config [ID:" + edgeId + "]"); + } catch (OpenemsException e) { + log.error("Unable to update Edge config [ID:" + edgeId + "]: " + e.getMessage()); + } } // public Optional getUser(int id) { diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java index 2d652fa4d25..e6d5a35b51a 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -16,14 +16,14 @@ public class OdooUtils { private OdooUtils() { } - private static Object[] executeKw(String url, Object[] params) throws XmlRpcException, MalformedURLException { + private static Object executeKw(String url, Object[] params) throws XmlRpcException, MalformedURLException { final XmlRpcClient client = new XmlRpcClient(); XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setEnabledForExtensions(true); config.setServerURL(new URL(String.format("%s/xmlrpc/2/object", url))); config.setReplyTimeout(5000); client.setConfig(config); - return (Object[]) client.execute("execute_kw", params); + return client.execute("execute_kw", params); } /** @@ -59,7 +59,7 @@ protected static int[] search(String url, String database, int uid, String passw Object[] params = new Object[] { database, uid, password, model, action, paramsDomain, paramsRules }; try { // Execute XML request - Object[] resultObjs = executeKw(url, params); + Object[] resultObjs = (Object[]) executeKw(url, params); // Parse results int[] results = new int[resultObjs.length]; for (int i = 0; i < resultObjs.length; i++) { @@ -92,7 +92,7 @@ protected static int[] search(String url, String database, int uid, String passw * @throws OpenemsException */ protected static Map readOne(String url, String database, int uid, String password, String model, - int id, Fields... fields) throws OpenemsException { + int id, Field... fields) throws OpenemsException { // Create request params String action = "read"; // Add ids @@ -109,7 +109,7 @@ protected static Map readOne(String url, String database, int ui Object[] params = new Object[] { database, uid, password, model, action, paramsIds, paramsFields }; try { // Execute XML request - Object[] resultObjs = executeKw(url, params); + Object[] resultObjs = (Object[]) executeKw(url, params); // Parse results for (int i = 0; i < resultObjs.length;) { @SuppressWarnings("unchecked") @@ -118,7 +118,7 @@ protected static Map readOne(String url, String database, int ui } throw new OpenemsException("No matching entry found for id [" + id + "]"); } catch (Throwable e) { - throw new OpenemsException("Unable to search from Odoo: " + e.getMessage()); + throw new OpenemsException("Unable to read from Odoo: " + e.getMessage()); } } @@ -143,7 +143,7 @@ protected static Map readOne(String url, String database, int ui * @throws OpenemsException */ protected static Map[] readMany(String url, String database, int uid, String password, String model, - int[] ids, Fields... fields) throws OpenemsException { + int[] ids, Field... fields) throws OpenemsException { // Create request params String action = "read"; // Add ids @@ -162,7 +162,7 @@ protected static Map[] readMany(String url, String database, int Object[] params = new Object[] { database, uid, password, model, action, paramsIds, paramsFields }; try { // Execute XML request - Object[] resultObjs = executeKw(url, params); + Object[] resultObjs = (Object[]) executeKw(url, params); // Parse results @SuppressWarnings("unchecked") Map[] results = (Map[]) new Map[resultObjs.length]; @@ -173,7 +173,53 @@ protected static Map[] readMany(String url, String database, int } return results; } catch (Throwable e) { - throw new OpenemsException("Unable to search from Odoo: " + e.getMessage()); + throw new OpenemsException("Unable to read from Odoo: " + e.getMessage()); + } + } + + /** + * Update a record in Odoo + * + * @param url + * URL of Odoo instance + * @param database + * Database name + * @param uid + * UID of user (e.g. '1' for admin) + * @param password + * Password of user + * @param model + * Odoo model to query (e.g. 'res.partner') + * @param id + * id of model to update + * @param fieldValues + * fields and values that should be written + * @return + * @throws OpenemsException + */ + protected static void write(String url, String database, int uid, String password, String model, int id, + FieldValue... fieldValues) throws OpenemsException { + // Create request params + String action = "write"; + // Add ids + Object[] paramsIds = new Object[1]; + paramsIds[0] = id; + // Add fieldValues + Map paramsFieldValues = new HashMap<>(); + for (FieldValue fieldValue : fieldValues) { + paramsFieldValues.put(fieldValue.getField().n(), fieldValue.getValue()); + } + // Create request params + Object[] params = new Object[] { database, uid, password, model, action, + new Object[] { paramsIds, paramsFieldValues } }; + try { + // Execute XML request + Boolean resultObj = (Boolean) executeKw(url, params); + if(!resultObj) { + throw new OpenemsException("Returned False."); + } + } catch (Throwable e) { + throw new OpenemsException("Unable to write to Odoo: " + e.getMessage()); } } } From bc3b59875c6fbbe0aabd84afa80c202ad2244392 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 19:31:28 +0100 Subject: [PATCH 066/156] Use common AbstractWebsocketServer --- .../provider/AbstractWebsocketServer.java | 205 ----------------- .../impl/provider/EdgeWebsocketServer.java | 1 + .../provider/AbstractWebsocketServer.java | 209 ------------------ .../websocket/AbstractWebsocketServer.java | 201 +++++++++-------- 4 files changed, 108 insertions(+), 508 deletions(-) delete mode 100644 io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java delete mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java deleted file mode 100644 index 1a24bcc61bd..00000000000 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/AbstractWebsocketServer.java +++ /dev/null @@ -1,205 +0,0 @@ -package io.openems.backend.edgewebsocket.impl.provider; - -import java.net.InetSocketAddress; -import java.util.Iterator; -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.java_websocket.drafts.Draft_6455; -import org.java_websocket.exceptions.WebsocketNotConnectedException; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.Lists; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.StringUtils; - -public abstract class AbstractWebsocketServer extends WebSocketServer { - private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); - - protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage); - - protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); - - protected abstract void _onError(WebSocket websocket, Exception ex); - - protected abstract void _onClose(WebSocket websocket); - - public AbstractWebsocketServer(int port) { - super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); - } - - @Override - public final void onStart() { - // nothing to do - } - - /** - * Open event of websocket. - */ - @Override - public final void onOpen(WebSocket websocket, ClientHandshake handshake) { - try { - this._onOpen(websocket, handshake); - } catch (Throwable e) { - log.error("onOpen-Error [" + this.handshakeToJsonObject(handshake) + "]: "); - e.printStackTrace(); - } - } - - /** - * Message event of websocket. Handles a new message. - */ - @Override - public final void onMessage(WebSocket websocket, String message) { - try { - JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); - this._onMessage(websocket, jMessage); - } catch (Throwable e) { - log.error("onMessage-Error [" + message + "]: " + e.getMessage()); - e.printStackTrace(); - } - } - - /** - * Close event of websocket. Removes the websocket. Keeps the session. Calls - * _onClose() - */ - @Override - public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { - // try { - // Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); - // String sessionString; - // if (sessionOpt.isPresent()) { - // sessionString = sessionOpt.get().toString() + " "; - // } else { - // sessionString = ""; - // } - // log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + - // reason + "]"); - // this.websockets.remove(websocket); - this._onClose(websocket); - // } catch (Throwable e) { - // log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + - // e.getMessage()); - // } - } - - /** - * Error event of websocket. Logs the error. - */ - @Override - public final void onError(WebSocket websocket, Exception ex) { - // S session = this.websockets.get(websocket); - // String sessionString; - // if (session == null) { - // sessionString = ""; - // } else { - // sessionString = session.toString(); - // } - // log.warn(sessionString + " Websocket error: " + ex.getMessage()); - this._onError(websocket, ex); - } - - /** - * Get cookie from handshake - * - * @param handshake - * @return cookie as JsonObject - */ - protected Optional getSessionIdFromHandshake(ClientHandshake handshake) { - JsonObject jCookie = new JsonObject(); - if (handshake.hasFieldValue("cookie")) { - String cookieString = handshake.getFieldValue("cookie"); - for (String cookieVariable : cookieString.split("; ")) { - String[] keyValue = cookieVariable.split("="); - if (keyValue.length == 2) { - jCookie.addProperty(keyValue[0], keyValue[1]); - } - } - } - return JsonUtils.getAsOptionalString(jCookie, "session_id"); - } - - /** - * Converts a Handshake to a JsonObject - * - * @param handshake - * @return - */ - protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { - JsonObject j = new JsonObject(); - for (Iterator iter = handshake.iterateHttpFields(); iter.hasNext();) { - String field = iter.next(); - j.addProperty(field, handshake.getFieldValue(field)); - } - return j; - } - - /** - * Returns the Websocket for the given token - * - * @param name - * @return - */ - // public Optional getWebsocketByToken(String token) { - // Optional sessionOpt = (Optional) - // this.sessionManager.getSessionByToken(token); - // if (!sessionOpt.isPresent()) { - // return Optional.empty(); - // } - // S session = sessionOpt.get(); - // return Optional.ofNullable(this.websockets.inverse().get(session)); - // } - // - // protected void addWebsocket(WebSocket websocket, S session) { - // synchronized (this.websockets) { - // if (this.websockets.containsKey(websocket)) { - // log.warn("Websocket [" + websocket + "] already existed. Replacing existing - // one with session [" - // + session + "]"); - // } - // if (this.websockets.inverse().containsKey(session)) { - // log.warn("Session [" + session + "] already existed. Replacing existing one - // with websocket [" - // + websocket + "]"); - // } - // this.websockets.forcePut(websocket, session); - // } - // } - // - // protected void removeWebsocket(WebSocket websocket) { - // synchronized (this.websockets) { - // this.websockets.remove(websocket); - // } - // } - // - // protected Optional getSessionFromWebsocket(WebSocket websocket) { - // return Optional.ofNullable(this.websockets.get(websocket)); - // } - // - // protected Optional getWebsocketFromSession(S session) { - // return Optional.ofNullable(this.websockets.inverse().get(session)); - // } - /** - * Send a message to a websocket - * - * @param j - * @return true if successful, otherwise false - */ - public boolean send(WebSocket websocket, JsonObject j) { - // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); - try { - websocket.send(j.toString()); - return true; - } catch (WebsocketNotConnectedException e) { - log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); - return false; - } - } -} diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index f09bf2a7ace..1e8935fb9aa 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -18,6 +18,7 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.utils.JsonUtils; import io.openems.common.utils.StringUtils; +import io.openems.common.websocket.AbstractWebsocketServer; import io.openems.common.websocket.DefaultMessages; import io.openems.common.websocket.WebSocketUtils; diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java deleted file mode 100644 index d3aa2379f0f..00000000000 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/AbstractWebsocketServer.java +++ /dev/null @@ -1,209 +0,0 @@ -package io.openems.backend.uiwebsocket.impl.provider; - -import java.net.InetSocketAddress; -import java.util.Iterator; -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.java_websocket.drafts.Draft_6455; -import org.java_websocket.exceptions.WebsocketNotConnectedException; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.Lists; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.StringUtils; - -public abstract class AbstractWebsocketServer extends WebSocketServer { - private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); - - protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt); - - protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); - - protected abstract void _onError(WebSocket websocket, Exception ex); - - protected abstract void _onClose(WebSocket websocket); - - public AbstractWebsocketServer(int port) { - super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); - } - - @Override - public final void onStart() { - // nothing to do - } - - /** - * Open event of websocket. - */ - @Override - public final void onOpen(WebSocket websocket, ClientHandshake handshake) { - try { - this._onOpen(websocket, handshake); - } catch (Throwable e) { - log.error("onOpen-Error [" + this.handshakeToJsonObject(handshake) + "]: "); - e.printStackTrace(); - } - } - - /** - * Message event of websocket. Handles a new message. - */ - @Override - public final void onMessage(WebSocket websocket, String message) { - try { - JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); - Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); - Optional deviceNameOpt = JsonUtils.getAsOptionalString(jMessage, "device"); - this._onMessage(websocket, jMessage, jMessageIdOpt, deviceNameOpt); - } catch (Throwable e) { - log.error("onMessage-Error [" + message + "]: " + e.getMessage()); - e.printStackTrace(); - } - } - - /** - * Close event of websocket. Removes the websocket. Keeps the session. Calls - * _onClose() - */ - @Override - public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { - // try { - // Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); - // String sessionString; - // if (sessionOpt.isPresent()) { - // sessionString = sessionOpt.get().toString() + " "; - // } else { - // sessionString = ""; - // } - // log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + - // reason + "]"); - // this.websockets.remove(websocket); - this._onClose(websocket); - // } catch (Throwable e) { - // log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + - // e.getMessage()); - // } - } - - /** - * Error event of websocket. Logs the error. - */ - @Override - public final void onError(WebSocket websocket, Exception ex) { - // S session = this.websockets.get(websocket); - // String sessionString; - // if (session == null) { - // sessionString = ""; - // } else { - // sessionString = session.toString(); - // } - // log.warn(sessionString + " Websocket error: " + ex.getMessage()); - this._onError(websocket, ex); - } - - /** - * Get cookie from handshake - * - * @param handshake - * @return cookie as JsonObject - */ - protected Optional getSessionIdFromHandshake(ClientHandshake handshake) { - JsonObject jCookie = new JsonObject(); - if (handshake.hasFieldValue("cookie")) { - String cookieString = handshake.getFieldValue("cookie"); - for (String cookieVariable : cookieString.split("; ")) { - String[] keyValue = cookieVariable.split("="); - if (keyValue.length == 2) { - jCookie.addProperty(keyValue[0], keyValue[1]); - } - } - } - return JsonUtils.getAsOptionalString(jCookie, "session_id"); - } - - /** - * Converts a Handshake to a JsonObject - * - * @param handshake - * @return - */ - protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { - JsonObject j = new JsonObject(); - for (Iterator iter = handshake.iterateHttpFields(); iter.hasNext();) { - String field = iter.next(); - j.addProperty(field, handshake.getFieldValue(field)); - } - return j; - } - - /** - * Returns the Websocket for the given token - * - * @param name - * @return - */ - // public Optional getWebsocketByToken(String token) { - // Optional sessionOpt = (Optional) - // this.sessionManager.getSessionByToken(token); - // if (!sessionOpt.isPresent()) { - // return Optional.empty(); - // } - // S session = sessionOpt.get(); - // return Optional.ofNullable(this.websockets.inverse().get(session)); - // } - // - // protected void addWebsocket(WebSocket websocket, S session) { - // synchronized (this.websockets) { - // if (this.websockets.containsKey(websocket)) { - // log.warn("Websocket [" + websocket + "] already existed. Replacing existing - // one with session [" - // + session + "]"); - // } - // if (this.websockets.inverse().containsKey(session)) { - // log.warn("Session [" + session + "] already existed. Replacing existing one - // with websocket [" - // + websocket + "]"); - // } - // this.websockets.forcePut(websocket, session); - // } - // } - // - // protected void removeWebsocket(WebSocket websocket) { - // synchronized (this.websockets) { - // this.websockets.remove(websocket); - // } - // } - // - // protected Optional getSessionFromWebsocket(WebSocket websocket) { - // return Optional.ofNullable(this.websockets.get(websocket)); - // } - // - // protected Optional getWebsocketFromSession(S session) { - // return Optional.ofNullable(this.websockets.inverse().get(session)); - // } - /** - * Send a message to a websocket - * - * @param j - * @return true if successful, otherwise false - */ - public boolean send(WebSocket websocket, JsonObject j) { - // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); - try { - websocket.send(j.toString()); - return true; - } catch (WebsocketNotConnectedException e) { - log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); - return false; - } - } -} diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java index b421929b02d..13269e7b64c 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java @@ -5,41 +5,39 @@ import java.util.Optional; import org.java_websocket.WebSocket; -import org.java_websocket.drafts.*; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import io.openems.common.session.Session; -import io.openems.common.session.SessionData; -import io.openems.common.session.SessionManager; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; -public abstract class AbstractWebsocketServer, D extends SessionData, M extends SessionManager> - extends WebSocketServer { +public abstract class AbstractWebsocketServer extends WebSocketServer { private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); - protected final M sessionManager; - private final BiMap websockets = Maps.synchronizedBiMap(HashBiMap.create()); - protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt); + protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage); protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); - protected abstract void _onClose(WebSocket websocket, Optional sessionOpt); + protected abstract void _onError(WebSocket websocket, Exception ex); - public AbstractWebsocketServer(int port, M sessionManager) { + protected abstract void _onClose(WebSocket websocket); + + public AbstractWebsocketServer(int port) { super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); - this.sessionManager = sessionManager; + } + + @Override + public final void onStart() { + // nothing to do } /** @@ -56,56 +54,57 @@ public final void onOpen(WebSocket websocket, ClientHandshake handshake) { } /** - * Close event of websocket. Removes the websocket. Keeps the session. Calls - * _onClose() + * Message event of websocket. Handles a new message. */ @Override - public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { + public final void onMessage(WebSocket websocket, String message) { try { - Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); - String sessionString; - if (sessionOpt.isPresent()) { - sessionString = sessionOpt.get().toString() + " "; - } else { - sessionString = ""; - } - log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + reason + "]"); - this.websockets.remove(websocket); - this._onClose(websocket, sessionOpt); + JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); + this._onMessage(websocket, jMessage); } catch (Throwable e) { - log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + e.getMessage()); + log.error("onMessage-Error [" + message + "]: " + e.getMessage()); + e.printStackTrace(); } } /** - * Error event of websocket. Logs the error. + * Close event of websocket. Removes the websocket. Keeps the session. Calls + * _onClose() */ @Override - public final void onError(WebSocket websocket, Exception ex) { - S session = this.websockets.get(websocket); - String sessionString; - if (session == null) { - sessionString = ""; - } else { - sessionString = session.toString(); - } - log.warn(sessionString + " Websocket error: " + ex.getMessage()); + public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { + // try { + // Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); + // String sessionString; + // if (sessionOpt.isPresent()) { + // sessionString = sessionOpt.get().toString() + " "; + // } else { + // sessionString = ""; + // } + // log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + + // reason + "]"); + // this.websockets.remove(websocket); + this._onClose(websocket); + // } catch (Throwable e) { + // log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + + // e.getMessage()); + // } } /** - * Message event of websocket. Handles a new message. + * Error event of websocket. Logs the error. */ @Override - public final void onMessage(WebSocket websocket, String message) { - try { - JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); - Optional jMessageId = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); - Optional deviceNameOpt = JsonUtils.getAsOptionalString(jMessage, "device"); - this._onMessage(websocket, jMessage, jMessageId, deviceNameOpt); - } catch (Throwable e) { - log.error("onMessage-Error [" + message + "]: " + e.getMessage()); - e.printStackTrace(); - } + public final void onError(WebSocket websocket, Exception ex) { + // S session = this.websockets.get(websocket); + // String sessionString; + // if (session == null) { + // sessionString = ""; + // } else { + // sessionString = session.toString(); + // } + // log.warn(sessionString + " Websocket error: " + ex.getMessage()); + this._onError(websocket, ex); } /** @@ -114,18 +113,18 @@ public final void onMessage(WebSocket websocket, String message) { * @param handshake * @return cookie as JsonObject */ - protected JsonObject parseCookieFromHandshake(ClientHandshake handshake) { - JsonObject j = new JsonObject(); + protected Optional getSessionIdFromHandshake(ClientHandshake handshake) { + JsonObject jCookie = new JsonObject(); if (handshake.hasFieldValue("cookie")) { String cookieString = handshake.getFieldValue("cookie"); for (String cookieVariable : cookieString.split("; ")) { String[] keyValue = cookieVariable.split("="); if (keyValue.length == 2) { - j.addProperty(keyValue[0], keyValue[1]); + jCookie.addProperty(keyValue[0], keyValue[1]); } } } - return j; + return JsonUtils.getAsOptionalString(jCookie, "session_id"); } /** @@ -143,51 +142,65 @@ protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { return j; } - @Override - public final void onStart() { - // nothing to do - } - /** * Returns the Websocket for the given token * * @param name * @return */ - public Optional getWebsocketByToken(String token) { - Optional sessionOpt = (Optional) this.sessionManager.getSessionByToken(token); - if (!sessionOpt.isPresent()) { - return Optional.empty(); - } - S session = sessionOpt.get(); - return Optional.ofNullable(this.websockets.inverse().get(session)); - } - - protected void addWebsocket(WebSocket websocket, S session) { - synchronized (this.websockets) { - if (this.websockets.containsKey(websocket)) { - log.warn("Websocket [" + websocket + "] already existed. Replacing existing one with session [" - + session + "]"); - } - if (this.websockets.inverse().containsKey(session)) { - log.warn("Session [" + session + "] already existed. Replacing existing one with websocket [" - + websocket + "]"); - } - this.websockets.forcePut(websocket, session); - } - } - - protected void removeWebsocket(WebSocket websocket) { - synchronized (this.websockets) { - this.websockets.remove(websocket); + // public Optional getWebsocketByToken(String token) { + // Optional sessionOpt = (Optional) + // this.sessionManager.getSessionByToken(token); + // if (!sessionOpt.isPresent()) { + // return Optional.empty(); + // } + // S session = sessionOpt.get(); + // return Optional.ofNullable(this.websockets.inverse().get(session)); + // } + // + // protected void addWebsocket(WebSocket websocket, S session) { + // synchronized (this.websockets) { + // if (this.websockets.containsKey(websocket)) { + // log.warn("Websocket [" + websocket + "] already existed. Replacing existing + // one with session [" + // + session + "]"); + // } + // if (this.websockets.inverse().containsKey(session)) { + // log.warn("Session [" + session + "] already existed. Replacing existing one + // with websocket [" + // + websocket + "]"); + // } + // this.websockets.forcePut(websocket, session); + // } + // } + // + // protected void removeWebsocket(WebSocket websocket) { + // synchronized (this.websockets) { + // this.websockets.remove(websocket); + // } + // } + // + // protected Optional getSessionFromWebsocket(WebSocket websocket) { + // return Optional.ofNullable(this.websockets.get(websocket)); + // } + // + // protected Optional getWebsocketFromSession(S session) { + // return Optional.ofNullable(this.websockets.inverse().get(session)); + // } + /** + * Send a message to a websocket + * + * @param j + * @return true if successful, otherwise false + */ + public boolean send(WebSocket websocket, JsonObject j) { + // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); + try { + websocket.send(j.toString()); + return true; + } catch (WebsocketNotConnectedException e) { + log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; } } - - protected Optional getSessionFromWebsocket(WebSocket websocket) { - return Optional.ofNullable(this.websockets.get(websocket)); - } - - protected Optional getWebsocketFromSession(S session) { - return Optional.ofNullable(this.websockets.inverse().get(session)); - } } From 5e760e56bc2a333fa37ce392175770fe946ad19b Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 19:32:11 +0100 Subject: [PATCH 067/156] Merge with develop --- ui/package.json | 2 +- ui/pom.xml | 2 +- ui/src/app/about/about.component.html | 6 +++--- ui/src/app/shared/device/config.ts | 16 ++++++++++++++-- ui/src/tsconfig.spec.json | 1 + ui/tslint.json | 1 + 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/ui/package.json b/ui/package.json index 14eafd9329d..fdec373a65e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "openems-ui", - "version": "1.8.0-SNAPSHOT", + "version": "2018.2-SNAPSHOT", "license": "AGPL", "scripts": { "ng": "ng", diff --git a/ui/pom.xml b/ui/pom.xml index e6c4ad5c1c9..d596fb58537 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -7,7 +7,7 @@ io.openems pom - 1.8.0-SNAPSHOT + 2018.2-SNAPSHOT edge pom diff --git a/ui/src/app/about/about.component.html b/ui/src/app/about/about.component.html index cfa0c59ecd2..9a1e5d81493 100644 --- a/ui/src/app/about/about.component.html +++ b/ui/src/app/about/about.component.html @@ -17,12 +17,12 @@ About.Sourcecode
    1. - About.Build: v1.8.0-SNAPSHOT + About.Build: v2018.2-SNAPSHOT
    2. diff --git a/ui/src/app/shared/device/config.ts b/ui/src/app/shared/device/config.ts index d1c20fac1da..adf0798fbc9 100644 --- a/ui/src/app/shared/device/config.ts +++ b/ui/src/app/shared/device/config.ts @@ -143,9 +143,20 @@ export class ConfigImpl implements DefaultTypes.Config { this.evcsDevices = evcsDevices; } + public getStateChannels(): DefaultTypes.ChannelAddresses { + let result: DefaultTypes.ChannelAddresses = {} + + // Set "ignoreNatures" + for (let thingId in this.config.things) { + result[thingId] = ["State"]; + } + return result; + } + + /** - * Return ChannelAddresses of power channels - */ + * Return ChannelAddresses of power channels + */ public getPowerChannels(): DefaultTypes.ChannelAddresses { let ignoreNatures = { EssClusterNature: true }; let result: DefaultTypes.ChannelAddresses = {} @@ -234,6 +245,7 @@ export class ConfigImpl implements DefaultTypes.Config { } } // basic channels + merge(this.getStateChannels()); merge(this.getPowerChannels()); merge(this.getEssSocChannels()); // widget channels diff --git a/ui/src/tsconfig.spec.json b/ui/src/tsconfig.spec.json index ac22a298aca..63d89ff283f 100644 --- a/ui/src/tsconfig.spec.json +++ b/ui/src/tsconfig.spec.json @@ -4,6 +4,7 @@ "outDir": "../out-tsc/spec", "baseUrl": "./", "module": "commonjs", + "target": "es5", "types": [ "jasmine", "node" diff --git a/ui/tslint.json b/ui/tslint.json index 9963d6c3954..a2e30eff294 100644 --- a/ui/tslint.json +++ b/ui/tslint.json @@ -107,6 +107,7 @@ "variable-declaration": "nospace" } ], + "typeof-compare": true, "unified-signatures": true, "variable-name": false, "whitespace": [ From a2be6080b6c3425c4728e351d06317a751fe1581 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 23:24:01 +0100 Subject: [PATCH 068/156] Adjust communication protocol: - Use simple string for messageId to UI; rename to messageId - Use edgeId for matching --- doc/readme.md | 11 +++--- .../common/websocket/DefaultMessages.java | 8 ++-- ui/src/app/shared/device/device.ts | 9 +++-- ui/src/app/shared/service/defaultmessages.ts | 6 +-- ui/src/app/shared/service/defaulttypes.ts | 1 + ui/src/app/shared/service/websocket.ts | 39 +++++++------------ 6 files changed, 32 insertions(+), 42 deletions(-) diff --git a/doc/readme.md b/doc/readme.md index b547feea360..8665656b572 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -56,7 +56,8 @@ currently forwarded to Odoo login page user: { id: Integer }, - devices?: [{ + edges?: [{ + id: number, name: string, comment: string, producttype: "Pro 9-12" | "MiniES 3-3" | "PRO Hybrid 9-10" | "PRO Compact 3-10" | "COMMERCIAL 40-45" | "INDUSTRIAL", @@ -85,7 +86,7 @@ Backend is transparently proxying requests to a connected Edge if necessary, add ``` { - id: [..., token], + messageId: [string] | string, role: "admin" | "installer" | "owner" | "guest", ... } @@ -97,8 +98,8 @@ Backend is transparently proxying requests to a connected Edge if necessary, add ``` { - device: string, - id: [UUID], + messageId: UUID, + edgeId: number, config: { mode: "query", language: 'de' | 'en' | ... @@ -110,7 +111,7 @@ Backend is transparently proxying requests to a connected Edge if necessary, add ``` { - id: [UUID], + messageId: UUID, config: { things: { [id: string]: { diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index c2e12c4e42b..c14f8e91df5 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -1,6 +1,5 @@ package io.openems.common.websocket; -import java.util.Collection; import java.util.HashMap; import java.util.Map.Entry; import java.util.Optional; @@ -9,7 +8,6 @@ import com.google.gson.JsonObject; import io.openems.common.types.ChannelAddress; -import io.openems.common.types.DeviceImpl; import io.openems.common.types.FieldValue; import io.openems.common.types.NumberFieldValue; import io.openems.common.types.StringFieldValue; @@ -24,6 +22,7 @@ public class DefaultMessages { * token: String * }, metadata: { * devices: [{ + * id: number * name: String, * comment: String, * producttype: String, @@ -50,7 +49,7 @@ public static JsonObject browserConnectionSuccessfulReply(String token, Optional JsonObject j = new JsonObject(); j.add("authenticate", jAuthenticate); JsonObject jMetadata = new JsonObject(); - jMetadata.add("devices", jEdges); + jMetadata.add("edges", jEdges); j.add("metadata", jMetadata); return j; } @@ -162,8 +161,9 @@ public static JsonObject timestampedData(long timestamp, HashMap }, + private replyStreams: { [messageId: string]: Subject }, private websocket: Websocket ) { // prepare stream/obersable for currentData @@ -74,8 +75,8 @@ export class Device { * Refresh the config */ public refreshConfig(): BehaviorSubject { - let message = DefaultMessages.configQuery(); - let messageId = message.id[0]; + let message = DefaultMessages.configQuery(this.id); + let messageId = message.messageId; this.replyStreams[messageId] = new Subject(); this.send(message); // wait for reply @@ -94,7 +95,7 @@ export class Device { * Sends a message to websocket */ public send(value: any): void { - this.websocket.send(value, this); + this.websocket.send(value); } /** diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts index 3ad797d8e5b..429ccd70c5c 100644 --- a/ui/src/app/shared/service/defaultmessages.ts +++ b/ui/src/app/shared/service/defaultmessages.ts @@ -23,10 +23,10 @@ export class DefaultMessages { }; }; - public static configQuery() { + public static configQuery(edgeId: number) { return { - device: String, - id: [UUID.UUID()], + messageId: UUID.UUID(), + edgeId: edgeId, config: { mode: "query", language: 'de' diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index 8d83942bd3a..7127fd7100d 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -81,6 +81,7 @@ export module DefaultTypes { } export interface MessageMetadataDevice { + id: number, name: string, comment: string, producttype: string, diff --git a/ui/src/app/shared/service/websocket.ts b/ui/src/app/shared/service/websocket.ts index baa8cf3f7b4..e61db474f76 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -20,6 +20,7 @@ import { DefaultMessages } from '../service/defaultmessages'; @Injectable() export class Websocket { public static readonly TIMEOUT = 5000; + private static readonly DEFAULT_EDGEID = 0; private static readonly DEFAULT_DEVICENAME = "fems"; // holds references of device names (=key) to Device objects (=value) @@ -172,7 +173,7 @@ export class Websocket { let replyStream: { [messageId: string]: Subject } = {}; this.replyStreams[Websocket.DEFAULT_DEVICENAME] = replyStream; this.devices.next({ - fems: new Device(Websocket.DEFAULT_DEVICENAME, "FEMS", "", role, true, replyStream, this) + fems: new Device(Websocket.DEFAULT_EDGEID, Websocket.DEFAULT_DEVICENAME, "FEMS", "", role, true, replyStream, this) }); } @@ -204,22 +205,13 @@ export class Websocket { /* * Query reply */ - if ("id" in message && message.id instanceof Array) { - let id = message.id[0]; - let deviceName = Websocket.DEFAULT_DEVICENAME; - if ("device" in message) { - // Receive a reply with a message id and a device -> forward to devices' replyStream - deviceName = message.device; - if (deviceName in this.replyStreams && id in this.replyStreams[deviceName]) { - this.replyStreams[deviceName][id].next(message); - } - } else { - // Receive a reply with a message id -> find device and forward to devices' replyStream - for (let deviceName in this.replyStreams) { - if (id in this.replyStreams[deviceName]) { - this.replyStreams[deviceName][id].next(message); - break; - } + if ("messageId" in message) { + // Receive a reply with a message id -> find device and forward to devices' replyStream + let messageId = message.messageId; + for (let deviceName in this.replyStreams) { + if (messageId in this.replyStreams[deviceName]) { + this.replyStreams[deviceName][messageId].next(message); + break; } } } @@ -228,13 +220,14 @@ export class Websocket { * Metadata */ if ("metadata" in message) { - if ("devices" in message.metadata) { - let devices = message.metadata.devices; + if ("edges" in message.metadata) { + let devices = message.metadata.edges; let newDevices = {}; for (let device of devices) { let replyStream: { [messageId: string]: Subject } = {}; this.replyStreams[device.name] = replyStream; let newDevice = new Device( + device.id, device.name, device.comment, device.producttype, @@ -340,16 +333,10 @@ export class Websocket { /** * Sends a message to the websocket */ - public send(message: any, device?: Device): void { + public send(message: any): void { if (env.debugMode) { console.info("SEND: ", message); } - if (device) { - if ("id" in message) { - this.pendingQueryReplies[message.id[0]] = device.name; - } - message["device"] = device.name; - } this.inputStream.next(JSON.stringify(message)); } } \ No newline at end of file From 9f734c199259bf4e240fdb6f02af154c793964ce Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 23:25:03 +0100 Subject: [PATCH 069/156] Handle Edge Config update inside Edge class via listener --- .../impl/provider/EdgeWebsocketServer.java | 7 ++- .../io/openems/backend/metadata/api/Edge.java | 34 ++++++++++--- .../backend/metadata/api/MetadataService.java | 9 ++-- .../backend/metadata/api/OnSetConfig.java | 7 +++ .../openems/backend/metadata/odoo/Field.java | 6 ++- .../openems/backend/metadata/odoo/Odoo.java | 49 ++++++++++++------- 6 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 1e8935fb9aa..812ec53ce7f 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -130,7 +130,12 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { try { JsonObject jConfig = JsonUtils.getAsJsonObject(jMessage, "config"); for (int edgeId : edgeIds) { - this.parent.metadataService.updateEdgeConfig(edgeId, jConfig); + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if(!edgeOpt.isPresent()) { + // TODO error unable to find Edge from ID + } else { + edgeOpt.get().setConfig(jConfig); + } } } catch (OpenemsException e) { log.error("Device [IDs:" + edgeIds + "] sent config. Unable to parse: " + e.getMessage()); 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 84c288c87e7..87d1145b22d 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 @@ -1,5 +1,7 @@ package io.openems.backend.metadata.api; +import java.util.Optional; + import com.google.gson.JsonObject; public class Edge { @@ -7,15 +9,16 @@ public class Edge { private String name; private String comment; private String producttype; + private JsonObject jConfig; private boolean isOnline; - public Edge(int id, String name, String comment, String producttype) { + public Edge(int id, String name, String comment, String producttype, JsonObject jConfig) { this.id = id; this.name = name; this.comment = comment; this.producttype = producttype; } - + public int getId() { return id; } @@ -23,27 +26,46 @@ public int getId() { public String getName() { return name; } - + /* * Marks this Edge as being online. This is called by an event listener. */ public void setOnline(boolean isOnline) { this.isOnline = isOnline; + // TODO call OnSetOnline } - + + private Optional onSetConfig = Optional.empty(); + + public void onSetConfig(OnSetConfig listener) { + this.onSetConfig = Optional.of(listener); + } + + public void setConfig(JsonObject jConfig) { + this.jConfig = jConfig; + if (this.onSetConfig.isPresent()) { + this.onSetConfig.get().call(jConfig); + } + } + + public JsonObject getConfig() { + return this.jConfig; + } + public boolean isOnline() { return this.isOnline; } - + public JsonObject toJsonObject() { JsonObject j = new JsonObject(); + j.addProperty("id", this.id); j.addProperty("name", this.name); j.addProperty("comment", this.comment); j.addProperty("producttype", this.producttype); j.addProperty("online", this.isOnline); return j; } - + @Override public String toString() { return "Edge [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 6d776035384..9b322a47574 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -4,19 +4,16 @@ import org.osgi.annotation.versioning.ProviderType; -import com.google.gson.JsonObject; - import io.openems.common.exceptions.OpenemsException; @ProviderType public interface MetadataService { - public abstract User getUserWithSession(String sessionId) throws OpenemsException; + public abstract Optional getUserWithSession(String sessionId) throws OpenemsException; public abstract int[] getEdgeIdsForApikey(String apikey); public abstract Optional getEdge(int edgeId); - - public abstract void updateEdgeConfig(int edgeId, JsonObject jConfig); - + + public abstract Optional getUser(int userId); } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java new file mode 100644 index 00000000000..8ff0b4c9ed5 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java @@ -0,0 +1,7 @@ +package io.openems.backend.metadata.api; + +import com.google.gson.JsonObject; + +public interface OnSetConfig { + public void call(JsonObject config); +} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java index 2cc7692d16f..e7127f5137e 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java @@ -4,7 +4,11 @@ public interface Field { public String n(); public enum FemsDevice implements Field { - ID("id"), NAME("name"), COMMENT("comment"), PRODUCT_TYPE("producttype"), OPENEMS_CONFIG("openems_config"); + ID("id"), // + NAME("name"), // + COMMENT("comment"), // + PRODUCT_TYPE("producttype"), // + OPENEMS_CONFIG("openems_config"); private final String n; diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 55d17b634cb..a56909c0b5d 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -90,7 +90,7 @@ void deactivate() { * @throws OpenemsException */ @Override - public User getUserWithSession(String sessionId) throws OpenemsException { + public Optional getUserWithSession(String sessionId) { HttpURLConnection connection = null; try { // send request to Odoo @@ -142,17 +142,19 @@ public User getUserWithSession(String sessionId) throws OpenemsException { synchronized (this.users) { this.users.put(user.getId(), user); } - return user; + return Optional.of(user); } } } catch (IOException e) { - throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); + log.error("IOException while reading from Odoo: " + e.getMessage()); + } catch (OpenemsException e) { + log.error("OpenemsException while reading from Odoo: " + e.getMessage()); } finally { if (connection != null) { connection.disconnect(); } } - throw new OpenemsException("No result from Odoo"); + return Optional.empty(); } @Override @@ -184,6 +186,14 @@ public Optional getEdge(int edgeId) { return this.getEdgeForceRefresh(edgeId); } + @Override + public Optional getUser(int userId) { + // try to read from cache + synchronized (this.users) { + return Optional.ofNullable(this.users.get(userId)); + } + } + /** * Reads the Edge object from Odoo and stores it in the cache * @@ -194,12 +204,27 @@ private Optional getEdgeForceRefresh(int edgeId) { try { Map edgeMap = OdooUtils.readOne(this.url, this.database, this.uid, this.password, "fems.device", edgeId, Field.FemsDevice.NAME, Field.FemsDevice.COMMENT, - Field.FemsDevice.PRODUCT_TYPE); + Field.FemsDevice.PRODUCT_TYPE, Field.FemsDevice.OPENEMS_CONFIG); Edge edge = new Edge( // (Integer) edgeMap.get(Field.FemsDevice.ID.n()), // (String) edgeMap.get(Field.FemsDevice.NAME.n()), // (String) edgeMap.get(Field.FemsDevice.COMMENT.n()), // - (String) edgeMap.get(Field.FemsDevice.PRODUCT_TYPE.n())); + (String) edgeMap.get(Field.FemsDevice.PRODUCT_TYPE.n()), // + JsonUtils.getAsJsonObject( + JsonUtils.parse((String) edgeMap.get(Field.FemsDevice.OPENEMS_CONFIG.n())))); + edge.onSetConfig(jConfig -> { + /* + * Update Edge config in Odoo + */ + try { + String config = new GsonBuilder().setPrettyPrinting().create().toJson(jConfig); + OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edgeId, + new FieldValue(Field.FemsDevice.OPENEMS_CONFIG, config)); + log.info("Updated Edge config [" + edge.getName() + "]"); + } catch (OpenemsException e) { + log.error("Unable to update Edge config [ID:" + edge.getName() + "]: " + e.getMessage()); + } + }); edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); // store in cache synchronized (this.edges) { @@ -212,18 +237,6 @@ private Optional getEdgeForceRefresh(int edgeId) { } } - @Override - public void updateEdgeConfig(int edgeId, JsonObject jConfig) { - try { - String config = new GsonBuilder().setPrettyPrinting().create().toJson(jConfig); - OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edgeId, - new FieldValue(Field.FemsDevice.OPENEMS_CONFIG, config)); - log.info("Updated Edge config [ID:" + edgeId + "]"); - } catch (OpenemsException e) { - log.error("Unable to update Edge config [ID:" + edgeId + "]: " + e.getMessage()); - } - } - // public Optional getUser(int id) { // return Optional.ofNullable(this.users.get(id)); // } From 1ff54c7aa3656c6d84d0befa16905310f59e5727 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 23:25:26 +0100 Subject: [PATCH 070/156] Implement config query message --- .../impl/provider/UiWebsocketServer.java | 127 ++++++++++++++++-- 1 file changed, 117 insertions(+), 10 deletions(-) diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 58590ef8de8..48de2449e08 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -1,7 +1,10 @@ package io.openems.backend.uiwebsocket.impl.provider; import java.util.Map.Entry; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; +import java.util.UUID; import org.java_websocket.WebSocket; import org.java_websocket.framing.CloseFrame; @@ -16,6 +19,8 @@ import io.openems.backend.metadata.api.Role; import io.openems.backend.metadata.api.User; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.AbstractWebsocketServer; import io.openems.common.websocket.DefaultMessages; import io.openems.common.websocket.WebSocketUtils; @@ -23,6 +28,7 @@ public class UiWebsocketServer extends AbstractWebsocketServer { private final Logger log = LoggerFactory.getLogger(UiWebsocketServer.class); private final UiWebsocket parent; + private final Map websocketsMap = new HashMap<>(); public UiWebsocketServer(UiWebsocket parent, int port) { super(port); @@ -32,7 +38,7 @@ public UiWebsocketServer(UiWebsocket parent, int port) { @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { String error = ""; - User user = null; + Optional userOpt = Optional.empty(); // login using session_id from the cookie Optional sessionIdOpt = getSessionIdFromHandshake(handshake); @@ -40,7 +46,8 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { error = "Session-ID is missing in handshake"; } else { try { - user = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); + userOpt = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); + // TODO fix bug in Odoo that is not reliably returning all configured devices } catch (OpenemsException e) { error = e.getMessage(); } @@ -51,8 +58,18 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { this.send(websocket, DefaultMessages.browserConnectionFailedReply()); log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + error + "]."); websocket.closeConnection(CloseFrame.REFUSE, error); - - } else if (user != null) { + + } else if (userOpt.isPresent()) { + User user = userOpt.get(); + + UUID uuid = UUID.randomUUID(); + synchronized (this.websocketsMap) { + // add websocket to local cache + this.websocketsMap.put(uuid, websocket); + } + // store userId together with the websocket + websocket.setAttachment(user.getId()); + // send connection successful to browser JsonArray jEdges = new JsonArray(); for (Entry edgeRole : user.getEdgeRoles().entrySet()) { @@ -74,12 +91,6 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { } } - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - log.info("UiWebsocketServer: On Message"); - } - @Override protected void _onError(WebSocket websocket, Exception ex) { log.info("UiWebsocketServer: On Error"); @@ -89,4 +100,100 @@ protected void _onError(WebSocket websocket, Exception ex) { protected void _onClose(WebSocket websocket) { log.info("UiWebsocketServer: On Close"); } + + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage) { + // get current User + int userId = websocket.getAttachment(); + + // get MessageId from message + Optional messageIdOpt = JsonUtils.getAsOptionalString(jMessage, "messageId"); + + // get EdgeId from message + Optional edgeIdOpt = JsonUtils.getAsOptionalInt(jMessage, "edgeId"); + + if (messageIdOpt.isPresent() && edgeIdOpt.isPresent()) { + String messageId = messageIdOpt.get(); + int edgeId = edgeIdOpt.get(); + + /* + * verify that User is allowed to access Edge + */ + Optional userOpt = this.parent.metadataService.getUser(userId); + if (!userOpt.isPresent() || !(userOpt.get().getEdgeRole(edgeId).isPresent())) { + // TODO Error Access denied + return; + } + + /* + * TODO Query historic data + */ + // if (jMessage.has("historicData")) { + // // parse deviceId + // JsonArray jMessageId = jMessageIdOpt.get(); + // try { + // JsonObject jHistoricData = JsonUtils.getAsJsonObject(jMessage, + // "historicData"); + // JsonObject jReply = WebSocketUtils.historicData(jMessageId, jHistoricData, + // deviceIdOpt, + // Timedata.instance(), Role.ADMIN); + // // TODO read role from device + // WebSocketUtils.send(websocket, jReply); + // } catch (OpenemsException e) { + // log.error(e.getMessage()); + // } + // } + + /* + * TODO Subscribe to currentData + */ + // if (jMessage.has("currentData")) { + // JsonObject jCurrentData; + // try { + // jCurrentData = JsonUtils.getAsJsonObject(jMessage, "currentData"); + // log.info("User [" + session.getData().getUserName() + "] subscribed to + // current data for device [" + // + deviceName + "]: " + StringUtils.toShortString(jCurrentData, 50)); + // JsonArray jMessageId = jMessageIdOpt.get(); + // int deviceId = deviceIdOpt.get(); + // this.currentData(session, websocket, jCurrentData, jMessageId, deviceName, + // deviceId); + // } catch (OpenemsException e) { + // log.error(e.getMessage()); + // } + // } + + /* + * Serve "Config -> Query" from cache + */ + Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); + if (jConfigOpt.isPresent()) { + JsonObject jConfig = jConfigOpt.get(); + switch (JsonUtils.getAsOptionalString(jConfig, "mode").orElse("")) { + case "query": + /* + * Query current config + */ + Edge edge = this.parent.metadataService.getEdge(edgeId).get(); + JsonObject jReply = DefaultMessages.configQueryReply(messageId, edge.getConfig()); + WebSocketUtils.send(websocket, jReply); + break; + } + + /* + * TODO Forward to OpenEMS Edge + */ + // if ((jMessage.has("config") && !configModeOpt.orElse("").equals("query")) || + // jMessage.has("log") + // || jMessage.has("system")) { + // try { + // forwardMessageToOpenems(session, websocket, jMessage, deviceName); + // } catch (OpenemsException e) { + // WebSocketUtils.sendNotification(websocket, new JsonArray(), + // LogBehaviour.WRITE_TO_LOG, + // Notification.EDGE_UNABLE_TO_FORWARD, deviceName, e.getMessage()); + // } + } + } + } } From b506a1ef414f4326138210aef44771279e63ea87 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 23:25:47 +0100 Subject: [PATCH 071/156] New getEdgeRole(int edgeId) method in User --- .../src/io/openems/backend/metadata/api/User.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java index 174a9464c8e..524d5080432 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.NavigableMap; +import java.util.Optional; import java.util.TreeMap; public class User { @@ -29,6 +30,10 @@ public void addEdgeRole(int deviceId, Role role) { public NavigableMap getEdgeRoles() { return Collections.unmodifiableNavigableMap(this.edgeRoles); } + + public Optional getEdgeRole(int edgeId) { + return Optional.ofNullable(this.edgeRoles.get(edgeId)); + } @Override public String toString() { From ce2864e327cdf886eb4edde9b12c181fb018a546 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 17 Feb 2018 23:38:51 +0100 Subject: [PATCH 072/156] Improve logs --- .../impl/provider/EdgeWebsocketServer.java | 2 +- .../impl/provider/UiWebsocketServer.java | 20 +++++++++++++---- .../impl/provider/WebsocketData.java | 22 +++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 812ec53ce7f..5a5a4a33814 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -138,7 +138,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { } } } catch (OpenemsException e) { - log.error("Device [IDs:" + edgeIds + "] sent config. Unable to parse: " + e.getMessage()); + log.error("Edge [IDs:" + edgeIds + "] sent config. Unable to parse: " + e.getMessage()); } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 48de2449e08..e2588dd0785 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -68,7 +68,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { this.websocketsMap.put(uuid, websocket); } // store userId together with the websocket - websocket.setAttachment(user.getId()); + websocket.setAttachment(new WebsocketData(user.getId(), uuid)); // send connection successful to browser JsonArray jEdges = new JsonArray(); @@ -98,14 +98,26 @@ protected void _onError(WebSocket websocket, Exception ex) { @Override protected void _onClose(WebSocket websocket) { - log.info("UiWebsocketServer: On Close"); + // get current User + WebsocketData data = websocket.getAttachment(); + Optional userOpt = this.parent.metadataService.getUser(data.getUserId()); + if (userOpt.isPresent()) { + log.info("User [" + userOpt.get().getName() + "] disconnected."); + } else { + log.info("User [ID:" + data.getUserId() + "] disconnected."); + } + // remove websocket from local cache + synchronized (this.websocketsMap) { + this.websocketsMap.remove(data.getUuid()); + } } @Override protected void _onMessage(WebSocket websocket, JsonObject jMessage) { // get current User - int userId = websocket.getAttachment(); - + WebsocketData data = websocket.getAttachment(); + int userId = data.getUserId(); + // get MessageId from message Optional messageIdOpt = JsonUtils.getAsOptionalString(jMessage, "messageId"); diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java new file mode 100644 index 00000000000..74567f461d2 --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java @@ -0,0 +1,22 @@ +package io.openems.backend.uiwebsocket.impl.provider; + +import java.util.UUID; + +public class WebsocketData { + private final int userId; + private final UUID uuid; + + public WebsocketData(int userId, UUID uuid) { + super(); + this.userId = userId; + this.uuid = uuid; + } + + public int getUserId() { + return userId; + } + + public UUID getUuid() { + return uuid; + } +} From 4a4cb38edc237f9512b1e27fd751f74122fb3584 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 18 Feb 2018 15:06:08 +0100 Subject: [PATCH 073/156] Add Influx + TimedataService --- .../BackendApp.bndrun | 7 +- .../bnd.bnd | 3 +- .../impl/provider/EdgeWebsocket.java | 4 + .../backend/timedata/api/TimedataService.java | 3 +- .../backend/timedata/influx/Influx.java | 243 +++++++++--------- .../websocket/AbstractWebsocketServer.java | 1 - 6 files changed, 136 insertions(+), 125 deletions(-) diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index c72bd40731f..bf27a20d85b 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -38,4 +38,9 @@ JPM-Command: openems-backend org.osgi.service.event;version='[1.3.1,1.3.2)',\ io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ - org.apache.servicemix.bundles.xmlrpc-client;version='[3.1.3,3.1.4)' \ No newline at end of file + org.apache.servicemix.bundles.xmlrpc-client;version='[3.1.3,3.1.4)',\ + io.openems.backend.timedata.influx.provider;version=snapshot,\ + org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ + org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ + org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)' \ No newline at end of file diff --git a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd index 15a58947fef..59f2a976fea 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd @@ -18,7 +18,8 @@ Export-Package: io.openems.backend.edgewebsocket.api io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ io.openems.backend.common;version=latest,\ - io.openems.backend.uiwebsocket.api;version=latest + io.openems.backend.uiwebsocket.api;version=latest,\ + io.openems.backend.timedata.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index ec65080523d..483e938b3de 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -14,6 +14,7 @@ import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.timedata.api.TimedataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; import org.osgi.service.metatype.annotations.Designate; @@ -32,6 +33,9 @@ public class EdgeWebsocket implements EdgeWebsocketService { @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) protected volatile UiWebsocketService uiWebsocketService; + + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected volatile TimedataService timedataService; @Reference protected EventAdmin eventAdmin; diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index a84fe8014f4..3fa74209f04 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -8,7 +8,6 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.OLD_MetadataDevices; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @@ -30,7 +29,7 @@ public interface TimedataService { * } * */ - public void write(OLD_MetadataDevices devices, JsonObject jData); + public void write(int edgeId, JsonObject jData) throws OpenemsException; public Optional getChannelValue(int deviceId, ChannelAddress channelAddress); diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index 44ce7d50eb0..812d326e7b6 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -5,7 +5,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.TreeMap; import java.util.concurrent.TimeUnit; import org.influxdb.InfluxDB; @@ -17,23 +16,19 @@ 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.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.TreeBasedTable; import com.google.gson.JsonArray; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.backend.metadata.api.OLD_MetadataDevice; -import io.openems.backend.metadata.api.OLD_MetadataDevices; import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; -import io.openems.common.utils.JsonUtils; - -import org.osgi.service.metatype.annotations.Designate; @Designate(ocd = Influx.Config.class, factory = false) @Component(name = "InfluxDB", configurationPolicy = ConfigurationPolicy.REQUIRE) @@ -105,120 +100,6 @@ private void connect() throws Exception { } } - /** - * Takes a JsonObject and writes the points to influxDB. - * - * Format: { "timestamp1" { "channel1": value, "channel2": value }, "timestamp2" - * { "channel1": value, "channel2": value } } - */ - @Override - public void write(OLD_MetadataDevices devices, JsonObject jData) { - TreeBasedTable data = TreeBasedTable.create(); - for (OLD_MetadataDevice device : devices) { - int deviceId = device.getIdOpt().orElse(0); - - // get existing or create new DeviceCache - DeviceCache deviceCache = this.deviceCacheMap.get(deviceId); - if (deviceCache == null) { - deviceCache = new DeviceCache(); - this.deviceCacheMap.put(deviceId, deviceCache); - } - - // Sort incoming data by timestamp - TreeMap sortedData = new TreeMap(); - for (Entry entry : jData.entrySet()) { - try { - Long timestamp = Long.valueOf(entry.getKey()); - JsonObject jChannels; - jChannels = JsonUtils.getAsJsonObject(entry.getValue()); - sortedData.put(timestamp, jChannels); - } catch (OpenemsException e) { - // TODO log.error("Data error: " + e.getMessage()); - System.out.println("Data error: " + e.getMessage()); - } - } - - // Prepare data table. Takes entries starting with eldest timestamp (ascending - // order) - for (Entry dataEntry : sortedData.entrySet()) { - Long timestamp = dataEntry.getKey(); - JsonObject jChannels = dataEntry.getValue(); - - if (jChannels.entrySet().size() == 0) { - // no channel values available. abort. - continue; - } - - // Check if cache is valid (it is not elder than 5 minutes compared to this - // timestamp) - long cacheTimestamp = deviceCache.getTimestamp(); - if (timestamp < cacheTimestamp) { - // incoming data is older than cache -> do not apply cache - } else { - // incoming data is more recent than cache - // update cache timestamp - deviceCache.setTimestamp(timestamp); - - if (timestamp < cacheTimestamp + 5 * 60 * 1000) { - // cache is valid (not elder than 5 minutes) - // add cache data to write data - for (Entry cacheEntry : deviceCache.getChannelCacheEntries()) { - String channel = cacheEntry.getKey(); - Object value = cacheEntry.getValue(); - data.put(timestamp, channel, value); - } - } else { - // cache is not anymore valid (elder than 5 minutes) - // clear cache - if (cacheTimestamp != 0l) { - System.out.println("Invalidate cache for device [" + deviceId + "]. This timestamp [" - + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); - // TODO log.info("Invalidate cache for device [" + deviceId + "]. This timestamp - // [" + - // timestamp - // + "]. Cache timestamp [" + cacheTimestamp + "]"); - } - deviceCache.clear(); - } - - // add incoming data to cache (this replaces already existing cache values) - for (Entry channelEntry : jChannels.entrySet()) { - String channel = channelEntry.getKey(); - Optional valueOpt = this.parseValue(channel, channelEntry.getValue()); - if (valueOpt.isPresent()) { - Object value = valueOpt.get(); - deviceCache.putToChannelCache(channel, value); - } - } - } - - // add incoming data to write data - for (Entry channelEntry : jChannels.entrySet()) { - String channel = channelEntry.getKey(); - Optional valueOpt = this.parseValue(channel, channelEntry.getValue()); - if (valueOpt.isPresent()) { - Object value = valueOpt.get(); - data.put(timestamp, channel, value); - } - } - } - - // Write data to default location - writeData(deviceId, data); - } - - // Hook to continue writing data to old Mini monitoring - // TODO remove after full migration - for ( - - OLD_MetadataDevice device : devices) { - if (device.getProductType().equals("MiniES 3-3")) { - writeDataToOldMiniMonitoring(device, data); - break; - } - } - } - // private void writeData(int deviceId, TreeBasedTable data) { BatchPoints batchPoints = BatchPoints.database(database) // @@ -369,4 +250,126 @@ public Optional getChannelValue(int deviceId, ChannelAddress channelAddr return Optional.empty(); } } + + /** + * Takes a JsonObject and writes the points to influxDB. + * + * Format: { "timestamp1" { "channel1": value, "channel2": value }, "timestamp2" + * { "channel1": value, "channel2": value } } + */ + @Override + public void write(int edgeId, JsonObject jData) throws OpenemsException { + // TODO + log.info("Would write..."); + // TreeBasedTable data = TreeBasedTable.create(); + // for (OLD_MetadataDevice device : devices) { + // int deviceId = device.getIdOpt().orElse(0); + // + // // get existing or create new DeviceCache + // DeviceCache deviceCache = this.deviceCacheMap.get(deviceId); + // if (deviceCache == null) { + // deviceCache = new DeviceCache(); + // this.deviceCacheMap.put(deviceId, deviceCache); + // } + // + // // Sort incoming data by timestamp + // TreeMap sortedData = new TreeMap(); + // for (Entry entry : jData.entrySet()) { + // try { + // Long timestamp = Long.valueOf(entry.getKey()); + // JsonObject jChannels; + // jChannels = JsonUtils.getAsJsonObject(entry.getValue()); + // sortedData.put(timestamp, jChannels); + // } catch (OpenemsException e) { + // // TODO log.error("Data error: " + e.getMessage()); + // System.out.println("Data error: " + e.getMessage()); + // } + // } + // + // // Prepare data table. Takes entries starting with eldest timestamp + // (ascending + // // order) + // for (Entry dataEntry : sortedData.entrySet()) { + // Long timestamp = dataEntry.getKey(); + // JsonObject jChannels = dataEntry.getValue(); + // + // if (jChannels.entrySet().size() == 0) { + // // no channel values available. abort. + // continue; + // } + // + // // Check if cache is valid (it is not elder than 5 minutes compared to this + // // timestamp) + // long cacheTimestamp = deviceCache.getTimestamp(); + // if (timestamp < cacheTimestamp) { + // // incoming data is older than cache -> do not apply cache + // } else { + // // incoming data is more recent than cache + // // update cache timestamp + // deviceCache.setTimestamp(timestamp); + // + // if (timestamp < cacheTimestamp + 5 * 60 * 1000) { + // // cache is valid (not elder than 5 minutes) + // // add cache data to write data + // for (Entry cacheEntry : deviceCache.getChannelCacheEntries()) + // { + // String channel = cacheEntry.getKey(); + // Object value = cacheEntry.getValue(); + // data.put(timestamp, channel, value); + // } + // } else { + // // cache is not anymore valid (elder than 5 minutes) + // // clear cache + // if (cacheTimestamp != 0l) { + // System.out.println("Invalidate cache for device [" + deviceId + "]. This + // timestamp [" + // + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); + // // TODO log.info("Invalidate cache for device [" + deviceId + "]. This + // timestamp + // // [" + + // // timestamp + // // + "]. Cache timestamp [" + cacheTimestamp + "]"); + // } + // deviceCache.clear(); + // } + // + // // add incoming data to cache (this replaces already existing cache values) + // for (Entry channelEntry : jChannels.entrySet()) { + // String channel = channelEntry.getKey(); + // Optional valueOpt = this.parseValue(channel, + // channelEntry.getValue()); + // if (valueOpt.isPresent()) { + // Object value = valueOpt.get(); + // deviceCache.putToChannelCache(channel, value); + // } + // } + // } + // + // // add incoming data to write data + // for (Entry channelEntry : jChannels.entrySet()) { + // String channel = channelEntry.getKey(); + // Optional valueOpt = this.parseValue(channel, + // channelEntry.getValue()); + // if (valueOpt.isPresent()) { + // Object value = valueOpt.get(); + // data.put(timestamp, channel, value); + // } + // } + // } + // + // // Write data to default location + // writeData(deviceId, data); + // } + // + // // Hook to continue writing data to old Mini monitoring + // // TODO remove after full migration + // for ( + // + // OLD_MetadataDevice device : devices) { + // if (device.getProductType().equals("MiniES 3-3")) { + // writeDataToOldMiniMonitoring(device, data); + // break; + // } + // } + } } diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java index 13269e7b64c..86deb78c5bc 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java @@ -13,7 +13,6 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; From 33f1cf4360e1ab70cf05dad01939d29f6ed02a05 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 18 Feb 2018 15:06:25 +0100 Subject: [PATCH 074/156] Write LastMessage timestamp to Odoo --- .../impl/provider/EdgeWebsocketServer.java | 92 ++++++++++++++++--- .../io/openems/backend/metadata/api/Edge.java | 16 ++++ .../metadata/api/OnSetLastMessage.java | 7 ++ .../openems/backend/metadata/odoo/Field.java | 3 +- .../openems/backend/metadata/odoo/Odoo.java | 14 ++- .../backend/metadata/odoo/OdooUtils.java | 13 ++- 6 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 5a5a4a33814..febc04ff4bc 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -126,19 +126,16 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * Config? -> store in Metadata */ - if (jMessage.has("config")) { - try { - JsonObject jConfig = JsonUtils.getAsJsonObject(jMessage, "config"); - for (int edgeId : edgeIds) { - Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); - if(!edgeOpt.isPresent()) { - // TODO error unable to find Edge from ID - } else { - edgeOpt.get().setConfig(jConfig); - } + Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); + if (jConfigOpt.isPresent()) { + JsonObject jConfig = jConfigOpt.get(); + for (int edgeId : edgeIds) { + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (!edgeOpt.isPresent()) { + // TODO error unable to find Edge from ID + } else { + edgeOpt.get().setConfig(jConfig); } - } catch (OpenemsException e) { - log.error("Edge [IDs:" + edgeIds + "] sent config. Unable to parse: " + e.getMessage()); } } @@ -154,9 +151,9 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * New timestamped data */ - if (jMessage.has("timedata")) { - log.info("TODO: timedata"); - // TODO timedata(devices, jMessage.get("timedata")); + Optional jTimedataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "timedata"); + if (jTimedataOpt.isPresent()) { + timedata(edgeIds, jTimedataOpt.get()); } } @@ -199,4 +196,69 @@ protected void _onClose(WebSocket websocket) { public boolean isOnline(int edgeId) { return this.websocketsMap.containsKey(edgeId); } + + private void timedata(int[] edgeIds, JsonObject jTimedata) { + for (int edgeId : edgeIds) { + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (edgeOpt.isPresent()) { + /* + * write data to timedataService + */ + Edge edge = edgeOpt.get(); + try { + this.parent.timedataService.write(edgeId, jTimedata); + log.debug("Edge [" + edge.getName() + "] wrote " + jTimedata.entrySet().size() + " timestamps " + + StringUtils.toShortString(jTimedata, 120)); + } catch (Exception e) { + log.error("Unable to write Timedata: ", e); + } + /* + * set last update timestamps in MetadataService + */ + edge.setLastMessage(); + } + } + + // TODO + // try { + // for (Entry jTimedataEntry : jTimedata.entrySet()) { + // try { + // JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue()); + // + // // set Odoo last update timestamp only for those channels + // for (String channel : jChannels.keySet()) { + // if (channel.endsWith("ActivePower") + // || channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2") + // | channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) { + // for (MetadataDevice device : devices) { + // device.setLastUpdate(); + // } + // } + // } + // + // // set specific Odoo values + // if (jChannels.has("ess0/Soc")) { + // int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt(); + // for (MetadataDevice device : devices) { + // device.setSoc(soc); + // } + // } + // if (jChannels.has("system0/PrimaryIpAddress")) { + // String ipv4 = JsonUtils.getAsPrimitive(jChannels, + // "system0/PrimaryIpAddress").getAsString(); + // for (MetadataDevice device : devices) { + // device.setIpV4(ipv4); + // } + // } + // } catch (OpenemsException e) { + // log.error("Device [" + String.join(",", devices.getNames()) + "] error: " + + // e.getMessage()); + // } + // } + // + // } catch (OpenemsException e) { + // log.error("Device [" + devices.getNamesString() + "] error: " + + // e.getMessage()); + // } + } } 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 87d1145b22d..81ec506fd9f 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 @@ -1,5 +1,7 @@ package io.openems.backend.metadata.api; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Optional; import com.google.gson.JsonObject; @@ -10,6 +12,7 @@ public class Edge { private String comment; private String producttype; private JsonObject jConfig; + private ZonedDateTime lastMessage = null; private boolean isOnline; public Edge(int id, String name, String comment, String producttype, JsonObject jConfig) { @@ -71,4 +74,17 @@ public String toString() { return "Edge [id=" + id + ", name=" + name + ", comment=" + comment + ", producttype=" + producttype + ", isOnline=" + isOnline + "]"; } + + private Optional onSetLastMessage = Optional.empty(); + + public void onSetLastMessage(OnSetLastMessage listener) { + this.onSetLastMessage = Optional.of(listener); + } + + public void setLastMessage() { + this.lastMessage = ZonedDateTime.now(ZoneOffset.UTC); + if (this.onSetLastMessage.isPresent()) { + this.onSetLastMessage.get().call(this.lastMessage); + } + } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java new file mode 100644 index 00000000000..ee60c1d0abd --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java @@ -0,0 +1,7 @@ +package io.openems.backend.metadata.api; + +import java.time.ZonedDateTime; + +public interface OnSetLastMessage { + public void call(ZonedDateTime lastMessage); +} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java index e7127f5137e..4723ef9ebae 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java @@ -8,7 +8,8 @@ public enum FemsDevice implements Field { NAME("name"), // COMMENT("comment"), // PRODUCT_TYPE("producttype"), // - OPENEMS_CONFIG("openems_config"); + OPENEMS_CONFIG("openems_config"), // + LAST_MESSAGE("lastmessage"); private final String n; diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index a56909c0b5d..a8859506f96 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -222,7 +222,19 @@ private Optional getEdgeForceRefresh(int edgeId) { new FieldValue(Field.FemsDevice.OPENEMS_CONFIG, config)); log.info("Updated Edge config [" + edge.getName() + "]"); } catch (OpenemsException e) { - log.error("Unable to update Edge config [ID:" + edge.getName() + "]: " + e.getMessage()); + log.error("Unable to update Edge [ID:" + edge.getName() + "] config: " + e.getMessage()); + } + }); + edge.onSetLastMessage(time -> { + /* + * Set LastMessage timestamp in Odoo + */ + // TODO only fire once per minute + try { + OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edge.getId(), + new FieldValue(Field.FemsDevice.LAST_MESSAGE, OdooUtils.DATETIME_FORMATTER.format(time))); + } catch (OpenemsException e) { + log.error("Unable to update Edge [ID:" + edge.getName() + "] lastMessage: " + e.getMessage()); } }); edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java index e6d5a35b51a..0e53e25b25a 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -2,6 +2,7 @@ import java.net.MalformedURLException; import java.net.URL; +import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; @@ -16,6 +17,14 @@ public class OdooUtils { private OdooUtils() { } + public final static String DEFAULT_SERVER_DATE_FORMAT = "yyyy-MM-dd"; + public final static String DEFAULT_SERVER_TIME_FORMAT = "HH:mm:ss"; + public final static String DEFAULT_SERVER_DATETIME_FORMAT = DEFAULT_SERVER_DATE_FORMAT + " " + + DEFAULT_SERVER_TIME_FORMAT; + + public final static DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter + .ofPattern(DEFAULT_SERVER_DATETIME_FORMAT); + private static Object executeKw(String url, Object[] params) throws XmlRpcException, MalformedURLException { final XmlRpcClient client = new XmlRpcClient(); XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); @@ -205,7 +214,7 @@ protected static void write(String url, String database, int uid, String passwor Object[] paramsIds = new Object[1]; paramsIds[0] = id; // Add fieldValues - Map paramsFieldValues = new HashMap<>(); + Map paramsFieldValues = new HashMap<>(); for (FieldValue fieldValue : fieldValues) { paramsFieldValues.put(fieldValue.getField().n(), fieldValue.getValue()); } @@ -215,7 +224,7 @@ protected static void write(String url, String database, int uid, String passwor try { // Execute XML request Boolean resultObj = (Boolean) executeKw(url, params); - if(!resultObj) { + if (!resultObj) { throw new OpenemsException("Returned False."); } } catch (Throwable e) { From c4331c0ed46bd0a4284d082459b4a3e8a1c3b51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 09:49:54 +0100 Subject: [PATCH 075/156] fix classpath --- edge/.classpath | 12 ------------ edge/.settings/org.eclipse.core.resources.prefs | 2 -- 2 files changed, 14 deletions(-) diff --git a/edge/.classpath b/edge/.classpath index 8577b7e5109..f8e9ecc84ee 100644 --- a/edge/.classpath +++ b/edge/.classpath @@ -14,18 +14,6 @@ - - - - - - - - - - - - diff --git a/edge/.settings/org.eclipse.core.resources.prefs b/edge/.settings/org.eclipse.core.resources.prefs index 2e41dd01076..22da5786879 100644 --- a/edge/.settings/org.eclipse.core.resources.prefs +++ b/edge/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,4 @@ eclipse.preferences.version=1 encoding//src/io/openems/impl/scheduler/SimpleScheduler.java=UTF-8 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 From 5531e4209ef524906fd9915356718e9ced922b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 09:51:51 +0100 Subject: [PATCH 076/156] add Synchronized to avoid concurrentModificationException --- .../core/utilities/power/SymmetricPower.java | 20 +- .../power/SymmetricPowerClusterImpl.java | 322 +++++++++--------- 2 files changed, 176 insertions(+), 166 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPower.java b/edge/src/io/openems/core/utilities/power/SymmetricPower.java index 4da13483941..5cc8e72f560 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPower.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPower.java @@ -27,7 +27,7 @@ public abstract class SymmetricPower { private static final SVGWriter writer = new SVGWriter(); private static final Color[] COLORS = new Color[] { Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW, Color.ORANGE, Color.RED }; - protected static final Coordinate ZERO = new Coordinate(0,0); + protected static final Coordinate ZERO = new Coordinate(0, 0); public static GeometryFactory getFactory() { return FACTORY; @@ -77,20 +77,18 @@ protected void setMaxApparentPower(long power) { } public Geometry getGeometry() { - // synchronized (this.geometry) { return this.geometry; - // } } protected void setGeometry(Geometry g) { - // synchronized (this.geometry) { this.geometry = g; this.geometries.add(g); this.calculateMinMax(); - for (PowerChangeListener listener : this.changeListeners) { - listener.powerChanged(g); + synchronized (this.changeListeners) { + for (PowerChangeListener listener : this.changeListeners) { + listener.powerChanged(g); + } } - // } } private void calculateMinMax() { @@ -137,11 +135,15 @@ public void removeListener(PowerResetListener listener) { } public void addListener(PowerChangeListener listener) { - this.changeListeners.add(listener); + synchronized (this.changeListeners) { + this.changeListeners.add(listener); + } } public void removeListener(PowerChangeListener listener) { - this.changeListeners.add(listener); + synchronized (this.changeListeners) { + this.changeListeners.add(listener); + } } public abstract void applyLimitation(Limitation limit) throws PowerException; diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java index 6a88b782a28..d524e96a231 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java @@ -44,14 +44,18 @@ public void onSchedulerInitialized() { } public void addEss(SymmetricEssNature ess) { - this.ess.add(ess); + synchronized (this.ess) { + this.ess.add(ess); + } mergePower(); ess.getPower().addListener(this); setMaxApparentPower(getMaxApparentPower() + ess.maxNominalPower().valueOptional().orElse(0L)); } public void removeEss(SymmetricEssNature ess) { - this.ess.remove(ess); + synchronized (this.ess) { + this.ess.remove(ess); + } mergePower(); ess.getPower().removeListener(this); setMaxApparentPower(getMaxApparentPower() - ess.maxNominalPower().valueOptional().orElse(0L)); @@ -64,8 +68,10 @@ public void powerChanged(Geometry allowedPower) { private void mergePower() { Geometry base = FACTORY.createPoint(new Coordinate(0, 0)); - for (SymmetricEssNature ess : this.ess) { - base = getUnionAround(base, ess.getPower().getGeometry()); + synchronized (this.ess) { + for (SymmetricEssNature ess : this.ess) { + base = getUnionAround(base, ess.getPower().getGeometry()); + } } synchronized (this.dynamicLimitations) { for (Limitation limit : this.dynamicLimitations) { @@ -114,168 +120,170 @@ public void applyLimitation(Limitation limit) throws PowerException { } private void setPower() { - Point p = reduceToZero(); - setGeometry(p); - long activePower = (long) p.getCoordinate().x; - long reactivePower = (long) p.getCoordinate().y; - long socSum = 0; - for (SymmetricEssNature ess : this.ess) { - socSum += ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0); - } - if (activePower > 0) { - /* - * Discharge - */ - // sort ess by useableSoc asc - Collections.sort(ess, (a, b) -> { - return (int) ((a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0)) - - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0))); - }); - for (int i = 0; i < ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minP = activePower; - for (int j = i + 1; j < this.ess.size(); j++) { - if (this.ess.get(j).soc().valueOptional().orElse(0L) - - this.ess.get(j).minSoc().valueOptional().orElse(0) > 0) { - minP -= this.ess.get(j).getPower().getMaxP().orElse(0L); - } - } - if (minP < 0) { - minP = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxP = ess.getPower().getMaxP().orElse(0L); - if (activePower < maxP) { - maxP = activePower; - } - double diff = maxP - minP; + synchronized (this.ess) { + Point p = reduceToZero(); + setGeometry(p); + long activePower = (long) p.getCoordinate().x; + long reactivePower = (long) p.getCoordinate().y; + long socSum = 0; + for (SymmetricEssNature ess : this.ess) { + socSum += ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0); + } + if (activePower > 0) { /* - * weight the range of possible power by the useableSoc - * if the useableSoc is negative the ess will be charged + * Discharge */ - long power = (long) (Math.ceil(minP + diff / socSum - * (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); - PEqualLimitation limit = new PEqualLimitation(ess.getPower()); - limit.setP(power); - try { - ess.getPower().applyLimitation(limit); - activePower -= power; - } catch (PowerException e) { - log.error("Failed to set activePower on " + ess.id()); - } - } - } else { - /* - * Charge - */ - /* - * sort ess by 100 - useabelSoc - * 100 - 90 = 10 - * 100 - 45 = 55 - * 100 - (- 5) = 105 - * => ess with negative useableSoc will be charged much more then one with positive useableSoc - */ - Collections.sort(this.ess, (a, b) -> { - return (int) ((100 - (a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0))) - - (100 - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0)))); - }); - for (int i = 0; i < this.ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minP = activePower; - for (int j = i + 1; j < this.ess.size(); j++) { - minP -= this.ess.get(j).getPower().getMinP().orElse(0L); - } - if (minP > 0) { - minP = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxP = ess.getPower().getMinP().orElse(0L); - if (activePower > maxP) { - maxP = activePower; - } - double diff = maxP - minP; - // weight the range of possible power by the useableSoc - long power = (long) Math.floor(minP + diff / (this.ess.size() * 100 - socSum) - * (100 - (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); - PEqualLimitation limit = new PEqualLimitation(ess.getPower()); - limit.setP(power); - try { - ess.getPower().applyLimitation(limit); - activePower -= power; - } catch (PowerException e) { - log.error("Failed to set activePower on " + ess.id()); - } - } - } - - // sort ess by maxNominalPower asc - Collections.sort(ess, (a, b) -> { - return (int) (a.maxNominalPower().valueOptional().orElse(0L) - - b.maxNominalPower().valueOptional().orElse(0L)); - }); - if (reactivePower > 0) { - for (int i = 0; i < ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minQ = reactivePower; - for (int j = i + 1; j < this.ess.size(); j++) { - if (this.ess.get(j).maxNominalPower().valueOptional().orElse(0L) > 0) { - minQ -= this.ess.get(j).getPower().getMaxQ().orElse(0L); + // sort ess by useableSoc asc + Collections.sort(ess, (a, b) -> { + return (int) ((a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0)) + - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0))); + }); + for (int i = 0; i < ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minP = activePower; + for (int j = i + 1; j < this.ess.size(); j++) { + if (this.ess.get(j).soc().valueOptional().orElse(0L) + - this.ess.get(j).minSoc().valueOptional().orElse(0) > 0) { + minP -= this.ess.get(j).getPower().getMaxP().orElse(0L); + } + } + if (minP < 0) { + minP = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxP = ess.getPower().getMaxP().orElse(0L); + if (activePower < maxP) { + maxP = activePower; + } + double diff = maxP - minP; + /* + * weight the range of possible power by the useableSoc + * if the useableSoc is negative the ess will be charged + */ + long power = (long) (Math.ceil(minP + diff / socSum + * (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); + PEqualLimitation limit = new PEqualLimitation(ess.getPower()); + limit.setP(power); + try { + ess.getPower().applyLimitation(limit); + activePower -= power; + } catch (PowerException e) { + log.error("Failed to set activePower on " + ess.id()); } } - if (minQ < 0) { - minQ = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxQ = ess.getPower().getMaxQ().orElse(0L); - if (reactivePower < maxQ) { - maxQ = reactivePower; - } - double diff = maxQ - minQ; + } else { /* - * weight the range of possible power by the useableSoc - * if the useableSoc is negative the ess will be charged + * Charge */ - long power = (long) (Math - .ceil(minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L))); - QEqualLimitation limit = new QEqualLimitation(ess.getPower()); - limit.setQ(power); - try { - ess.getPower().applyLimitation(limit); - reactivePower -= power; - } catch (PowerException e) { - log.error("Failed to set reactivePower on " + ess.id()); + /* + * sort ess by 100 - useabelSoc + * 100 - 90 = 10 + * 100 - 45 = 55 + * 100 - (- 5) = 105 + * => ess with negative useableSoc will be charged much more then one with positive useableSoc + */ + Collections.sort(this.ess, (a, b) -> { + return (int) ((100 - (a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0))) + - (100 - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0)))); + }); + for (int i = 0; i < this.ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minP = activePower; + for (int j = i + 1; j < this.ess.size(); j++) { + minP -= this.ess.get(j).getPower().getMinP().orElse(0L); + } + if (minP > 0) { + minP = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxP = ess.getPower().getMinP().orElse(0L); + if (activePower > maxP) { + maxP = activePower; + } + double diff = maxP - minP; + // weight the range of possible power by the useableSoc + long power = (long) Math.floor(minP + diff / (this.ess.size() * 100 - socSum) + * (100 - (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); + PEqualLimitation limit = new PEqualLimitation(ess.getPower()); + limit.setP(power); + try { + ess.getPower().applyLimitation(limit); + activePower -= power; + } catch (PowerException e) { + log.error("Failed to set activePower on " + ess.id()); + } } } - } else { - for (int i = 0; i < this.ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minQ = reactivePower; - for (int j = i + 1; j < this.ess.size(); j++) { - minQ -= this.ess.get(j).getPower().getMinQ().orElse(0L); - } - if (minQ > 0) { - minQ = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxQ = ess.getPower().getMinQ().orElse(0L); - if (reactivePower > maxQ) { - maxQ = reactivePower; + + // sort ess by maxNominalPower asc + Collections.sort(ess, (a, b) -> { + return (int) (a.maxNominalPower().valueOptional().orElse(0L) + - b.maxNominalPower().valueOptional().orElse(0L)); + }); + if (reactivePower > 0) { + for (int i = 0; i < ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minQ = reactivePower; + for (int j = i + 1; j < this.ess.size(); j++) { + if (this.ess.get(j).maxNominalPower().valueOptional().orElse(0L) > 0) { + minQ -= this.ess.get(j).getPower().getMaxQ().orElse(0L); + } + } + if (minQ < 0) { + minQ = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxQ = ess.getPower().getMaxQ().orElse(0L); + if (reactivePower < maxQ) { + maxQ = reactivePower; + } + double diff = maxQ - minQ; + /* + * weight the range of possible power by the useableSoc + * if the useableSoc is negative the ess will be charged + */ + long power = (long) (Math.ceil( + minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L))); + QEqualLimitation limit = new QEqualLimitation(ess.getPower()); + limit.setQ(power); + try { + ess.getPower().applyLimitation(limit); + reactivePower -= power; + } catch (PowerException e) { + log.error("Failed to set reactivePower on " + ess.id()); + } } - double diff = maxQ - minQ; - // weight the range of possible power by the useableSoc - long power = (long) Math - .floor(minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L)); - QEqualLimitation limit = new QEqualLimitation(ess.getPower()); - limit.setQ(power); - try { - ess.getPower().applyLimitation(limit); - reactivePower -= power; - } catch (PowerException e) { - log.error("Failed to set reactivePower on " + ess.id()); + } else { + for (int i = 0; i < this.ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minQ = reactivePower; + for (int j = i + 1; j < this.ess.size(); j++) { + minQ -= this.ess.get(j).getPower().getMinQ().orElse(0L); + } + if (minQ > 0) { + minQ = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxQ = ess.getPower().getMinQ().orElse(0L); + if (reactivePower > maxQ) { + maxQ = reactivePower; + } + double diff = maxQ - minQ; + // weight the range of possible power by the useableSoc + long power = (long) Math.floor( + minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L)); + QEqualLimitation limit = new QEqualLimitation(ess.getPower()); + limit.setQ(power); + try { + ess.getPower().applyLimitation(limit); + reactivePower -= power; + } catch (PowerException e) { + log.error("Failed to set reactivePower on " + ess.id()); + } } } } From bed671ee8dba5e5f5c826ff12f85b1d0d198a4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 09:52:56 +0100 Subject: [PATCH 077/156] improve power moderation --- .../utilities/power/SymmetricPowerImpl.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java index 1ae62f489cc..b91baa6fe0f 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java @@ -12,7 +12,6 @@ import io.openems.api.bridge.BridgeEventListener; import io.openems.api.channel.WriteChannel; import io.openems.api.exception.WriteChannelException; -import io.openems.core.utilities.AvgFiFoQueue; public class SymmetricPowerImpl extends SymmetricPower implements LimitationChangedListener, BridgeEventListener { /* @@ -25,8 +24,8 @@ public class SymmetricPowerImpl extends SymmetricPower implements LimitationChan private List staticLimitations; private List dynamicLimitations; - private AvgFiFoQueue activePowerAvg; - private AvgFiFoQueue reactivePowerAvg; + private long lastActivePower = 0; + private long lastReactivePower = 0; public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePower, WriteChannel setReactivePower, Bridge bridge) { @@ -35,8 +34,6 @@ public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePow this.dynamicLimitations = new ArrayList<>(); this.setActivePower = setActivePower; this.setReactivePower = setReactivePower; - activePowerAvg = new AvgFiFoQueue(3, 1.5); - reactivePowerAvg = new AvgFiFoQueue(1, 1.5); if(bridge != null) { bridge.addListener(this); }else { @@ -80,11 +77,13 @@ private void writePower() { Point p = reduceToZero(); Coordinate c = p.getCoordinate(); setGeometry(p); - activePowerAvg.add((long) c.x); - reactivePowerAvg.add((long) c.y); + long activePowerDelta = (long) (c.x - lastActivePower); + long reactivePowerDelta = (long) (c.y - lastReactivePower); + lastActivePower += activePowerDelta/2; + lastReactivePower += reactivePowerDelta/2; try { - this.setActivePower.pushWrite(activePowerAvg.avg()); - this.setReactivePower.pushWrite(reactivePowerAvg.avg()); + this.setActivePower.pushWrite(lastActivePower); + this.setReactivePower.pushWrite(lastReactivePower); setActivePower.shadowCopyAndReset(); setReactivePower.shadowCopyAndReset(); } catch (WriteChannelException e) { From ef221e2b0eab9933851ea62bb1f9fa093e8dcfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 09:54:01 +0100 Subject: [PATCH 078/156] add allowedCharge/-Discharge reduction by soc --- .../simulator/SimulatorSymmetricEss.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index b6fc3993a94..e122edaefd1 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -278,8 +278,26 @@ protected void update() { this.activePower.updateValue(activePower); this.reactivePower.updateValue(reactivePower); this.apparentPower.updateValue(apparentPower); - this.allowedCharge.updateValue(-9000L); - this.allowedDischarge.updateValue(3000L); + this.allowedCharge.updateValue(-50000L); + this.allowedDischarge.updateValue(50000L); + try { + long multiplier = 100 - this.soc.value(); + if (multiplier > 10) { + multiplier = 10; + } + this.allowedCharge.updateValue((maxNominalPower.value() / 10 * multiplier) * -1); + } catch (InvalidValueException e) { + e.printStackTrace(); + } + try { + long multiplier = this.soc.value(); + if (multiplier > 10) { + multiplier = 10; + } + this.allowedDischarge.updateValue(maxNominalPower.value() / 10 * multiplier); + } catch (InvalidValueException e) { + e.printStackTrace(); + } } @Override From 967dd3a1461dd8dba432c9268c09d9171af43329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 09:55:01 +0100 Subject: [PATCH 079/156] Charge Ess once completely before start surplus grid feed in --- .../BalancingSurplusController.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java index 12a0021ee5c..83fcec5a3c9 100644 --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java @@ -72,13 +72,9 @@ public void run() { try { Ess ess = this.ess.value(); // Calculate required sum values - long calculatedPower = meter.value().activePower.value() + (ess.activePower.value() - surplus); - surplus = getSurplusPower(); - // in case the storage has surplus it isn't allowed to charge the storage ac - if (calculatedPower < 0 && surplus > 0) { - calculatedPower = 0; - } - if (getPvVoltage() < 200000 || surplus < 0) { + long calculatedPower = meter.value().activePower.value() + ess.activePower.value(); + surplus = getSurplusPower()- calculatedPower; + if (surplus < 0) { surplus = 0l; } calculatedPower += surplus; @@ -93,9 +89,9 @@ public void run() { private long getSurplusPower() throws InvalidValueException { long power = 0l; - if (ess.value().soc.value() >= surplusMinSoc.value() + 2) { + if (ess.value().allowedCharge.value() >= -100 && getPvVoltage() >= 250000) { surplusOn = true; - } else if (ess.value().soc.value() < surplusMinSoc.value()) { + } else if (ess.value().soc.value() < surplusMinSoc.value() || getPvVoltage() < 200000) { surplusOn = false; } if (surplusOn) { From 31338a75bc29bfc1af79f96ddd41c1240b17e7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 09:56:09 +0100 Subject: [PATCH 080/156] Add GridFeedInLimitationController This Controller calculates the maximal power for the Ess to avoid overpower for the Grid --- .../symmetric/gridfeedinlimitation/Ess.java | 45 +++++++++++ .../GridFeedInLimitationController.java | 81 +++++++++++++++++++ .../symmetric/gridfeedinlimitation/Meter.java | 38 +++++++++ 3 files changed, 164 insertions(+) create mode 100644 edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java create mode 100644 edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java create mode 100644 edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Meter.java diff --git a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java new file mode 100644 index 00000000000..9ec34d5d98f --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.gridfeedinlimitation; + +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.SymmetricEssNature; +import io.openems.core.utilities.power.PSmallerEqualLimitation; +import io.openems.core.utilities.power.SymmetricPower; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel activePower; + public final SymmetricPower power; + public final PSmallerEqualLimitation limit; + + public Ess(SymmetricEssNature ess) { + super(ess); + + activePower = ess.activePower().required(); + this.power = ess.getPower(); + this.limit = new PSmallerEqualLimitation(power); + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java new file mode 100644 index 00000000000..3b74d7528c0 --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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.symmetric.gridfeedinlimitation; + +import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.thingstate.ThingStateChannel; +import io.openems.api.controller.Controller; +import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.InvalidValueException; +import io.openems.core.utilities.power.PowerException; + +@ThingInfo(title = "", description = "Calulates maximal Ess power to avoid grid feed in.") +public class GridFeedInLimitationController extends Controller { + + private ThingStateChannel thingState = new ThingStateChannel(this); + + /* + * Constructors + */ + public GridFeedInLimitationController() { + super(); + } + + public GridFeedInLimitationController(String thingId) { + super(thingId); + } + + /* + * Config + */ + @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) + public final ConfigChannel ess = new ConfigChannel("ess", this); + + @ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class) + public final ConfigChannel meter = new ConfigChannel("meter", this); + + @ChannelInfo(title = "Max GridFeedIn", description = "The max allowed power feed in to grid.", type = Long.class) + public final ConfigChannel maxGridFeedIn = new ConfigChannel<>("maxGridFeedIn", this); + + /* + * Methods + */ + + @Override + public void run() { + try { + long maxPower = ess.value().activePower.value() + meter.value().activePower.value() + maxGridFeedIn.value(); + ess.value().limit.setP(maxPower); + ess.value().power.applyLimitation(ess.value().limit); + } catch (InvalidValueException e) { + log.error(e.getMessage()); + } catch (PowerException e) { + log.error("Failed to set Power!", e); + } + } + + @Override + public ThingStateChannel getStateChannel() { + return this.thingState; + } + +} diff --git a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Meter.java b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Meter.java new file mode 100644 index 00000000000..fa365fb50ea --- /dev/null +++ b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Meter.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.symmetric.gridfeedinlimitation; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.meter.SymmetricMeterNature; + +@IsThingMap(type = SymmetricMeterNature.class) +public class Meter extends ThingMap { + + public final ReadChannel activePower; + + public Meter(SymmetricMeterNature meter) { + super(meter); + activePower = meter.activePower().required(); + } + +} From c872aac993bfabc33c72dc60cc18c6a1a78caefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 11:13:15 +0100 Subject: [PATCH 081/156] Use ISO Date Format and add Period for discharge --- .../AvoidTotalDischargeController.java | 42 +++++++++++++------ .../AvoidTotalDischargeController.java | 42 +++++++++++++------ 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 34771426377..c1ba0db02ac 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -20,9 +20,8 @@ *******************************************************************************/ package io.openems.impl.controller.asymmetric.avoidtotaldischarge; -import java.time.Instant; import java.time.LocalDate; -import java.time.ZoneId; +import java.time.Period; import java.util.Optional; import java.util.Set; @@ -60,12 +59,15 @@ public AvoidTotalDischargeController(String id) { public final ConfigChannel> esss = new ConfigChannel>("esss", this); @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); - @ChannelInfo(title = "Last Discharge", description = "Last Time, the ess was discharged completely.", type = Long.class,defaultValue = "0") - public final ConfigChannel lastDischarge = new ConfigChannel("lastDischarge", this).addChangeListener(this); - @ChannelInfo(title = "Enable Monthly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") - public final ConfigChannel enableMonthlyDischarge = new ConfigChannel("EnableMonthlyDischarge",this); + @ChannelInfo(title = "Next Discharge", description = "Next Time, the ess will discharge completely.", type = String.class,defaultValue = "2018-03-09") + public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); + @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class,defaultValue = "P4W") + public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); + @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") + public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge",this); - private LocalDate lastDischargeDate; + private LocalDate nextDischargeDate; + private Period period; /* * Methods @@ -129,7 +131,7 @@ public void run() { ess.currentState = State.CHARGESOC; } else if (ess.soc.value() >= ess.minSoc.value() + 5) { ess.currentState = State.NORMAL; - }else if(lastDischargeDate != null && lastDischargeDate.plusMonths(1).isBefore(LocalDate.now()) && enableMonthlyDischarge.valueOptional().isPresent() && enableMonthlyDischarge.valueOptional().get()) { + }else if(nextDischargeDate != null && nextDischargeDate.equals(LocalDate.now()) && enableDischarge.valueOptional().isPresent() && enableDischarge.valueOptional().get()) { ess.currentState = State.EMPTY; } else { try { @@ -182,7 +184,7 @@ public void run() { case EMPTY: if(ess.allowedDischarge.value() == 0) { //Ess is Empty set Date and charge to minSoc - lastDischarge.updateValue(System.currentTimeMillis(), true); + addPeriod(); ess.currentState = State.CHARGESOC; } break; @@ -195,12 +197,28 @@ public void run() { @Override public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if(this.lastDischarge.equals(channel)) { + if(this.nextDischarge.equals(channel)) { if(newValue.isPresent()) { - lastDischargeDate = Instant.ofEpochMilli((long) newValue.get()).atZone(ZoneId.systemDefault()).toLocalDate(); + nextDischargeDate = LocalDate.parse((String)newValue.get()); }else { - lastDischargeDate = null; + nextDischargeDate = null; } + }else if(this.dischargePeriod.equals(channel)) { + if(newValue.isPresent()) { + this.period = Period.parse((String)newValue.get()); + }else { + this.period = null; + } + } + if(nextDischargeDate != null && nextDischargeDate.isBefore(LocalDate.now())) { + addPeriod(); + } + } + + private void addPeriod() { + if(this.nextDischargeDate != null && this.period != null) { + this.nextDischargeDate.plus(period); + nextDischarge.updateValue(this.nextDischargeDate.toString(),true); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 4c9a8f1d9d3..dacf3fd6d0b 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -20,9 +20,8 @@ *******************************************************************************/ package io.openems.impl.controller.symmetric.avoidtotaldischarge; -import java.time.Instant; import java.time.LocalDate; -import java.time.ZoneId; +import java.time.Period; import java.util.Optional; import java.util.Set; @@ -60,12 +59,15 @@ public AvoidTotalDischargeController(String thingId) { public final ConfigChannel> esss = new ConfigChannel>("esss", this); @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); - @ChannelInfo(title = "Last Discharge", description = "Last Time, the ess was discharged completely.", type = Long.class,defaultValue = "0") - public final ConfigChannel lastDischarge = new ConfigChannel("lastDischarge", this).addChangeListener(this); - @ChannelInfo(title = "Enable Monthly Discharge", description="This option allowes the system once per month to discharge the ess completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") - public final ConfigChannel enableMonthlyDischarge = new ConfigChannel("EnableMonthlyDischarge",this); + @ChannelInfo(title = "Next Discharge", description = "Next Time, the ess will discharge completely.", type = String.class,defaultValue = "2018-03-09") + public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); + @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class,defaultValue = "P4W") + public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); + @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") + public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge",this); - private LocalDate lastDischargeDate; + private LocalDate nextDischargeDate; + private Period period; /* * Methods @@ -106,7 +108,7 @@ public void run() { ess.currentState = State.CHARGESOC; } else if (ess.soc.value() >= ess.minSoc.value() + 5) { ess.currentState = State.NORMAL; - }else if(lastDischargeDate != null && lastDischargeDate.plusMonths(1).isBefore(LocalDate.now()) && enableMonthlyDischarge.valueOptional().isPresent() && enableMonthlyDischarge.valueOptional().get()) { + }else if(nextDischargeDate != null && nextDischargeDate.equals(LocalDate.now()) && enableDischarge.valueOptional().isPresent() && enableDischarge.valueOptional().get()) { ess.currentState = State.EMPTY; } else { try { @@ -142,7 +144,7 @@ public void run() { case EMPTY: if(ess.allowedDischarge.value() == 0) { //Ess is Empty set Date and charge to minSoc - lastDischarge.updateValue(System.currentTimeMillis(), true); + addPeriod(); ess.currentState = State.CHARGESOC; } break; @@ -158,12 +160,28 @@ public void run() { @Override public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if(this.lastDischarge.equals(channel)) { + if(this.nextDischarge.equals(channel)) { if(newValue.isPresent()) { - lastDischargeDate = Instant.ofEpochMilli((long) newValue.get()).atZone(ZoneId.systemDefault()).toLocalDate(); + nextDischargeDate = LocalDate.parse((String)newValue.get()); }else { - lastDischargeDate = null; + nextDischargeDate = null; } + }else if(this.dischargePeriod.equals(channel)) { + if(newValue.isPresent()) { + this.period = Period.parse((String)newValue.get()); + }else { + this.period = null; + } + } + if(nextDischargeDate != null && nextDischargeDate.isBefore(LocalDate.now())) { + addPeriod(); + } + } + + private void addPeriod() { + if(this.nextDischargeDate != null && this.period != null) { + this.nextDischargeDate.plus(period); + nextDischarge.updateValue(this.nextDischargeDate.toString(),true); } } From e808ea88f88d06b7ff2ab0c13f7c9d4f3b9171be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 11:22:18 +0100 Subject: [PATCH 082/156] add Check for soc <1 --- .../avoidtotaldischarge/AvoidTotalDischargeController.java | 2 +- .../avoidtotaldischarge/AvoidTotalDischargeController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index c1ba0db02ac..be01ae7d4f9 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -182,7 +182,7 @@ public void run() { } break; case EMPTY: - if(ess.allowedDischarge.value() == 0) { + if(ess.allowedDischarge.value() == 0 || ess.soc.value() < 1) { //Ess is Empty set Date and charge to minSoc addPeriod(); ess.currentState = State.CHARGESOC; diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index dacf3fd6d0b..84bda7418f3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -142,7 +142,7 @@ public void run() { } break; case EMPTY: - if(ess.allowedDischarge.value() == 0) { + if(ess.allowedDischarge.value() == 0 || ess.soc.value() < 1) { //Ess is Empty set Date and charge to minSoc addPeriod(); ess.currentState = State.CHARGESOC; From cdb0158484f9a2af2f528217f08c91fc44dddcf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 12:43:33 +0100 Subject: [PATCH 083/156] fix period is null --- .../AvoidTotalDischargeController.java | 17 +++++------------ .../AvoidTotalDischargeController.java | 17 +++++------------ 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index be01ae7d4f9..b4972f214a9 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -60,14 +60,13 @@ public AvoidTotalDischargeController(String id) { @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); @ChannelInfo(title = "Next Discharge", description = "Next Time, the ess will discharge completely.", type = String.class,defaultValue = "2018-03-09") - public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); + public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class,defaultValue = "P4W") - public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); + public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge",this); private LocalDate nextDischargeDate; - private Period period; /* * Methods @@ -203,12 +202,6 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol }else { nextDischargeDate = null; } - }else if(this.dischargePeriod.equals(channel)) { - if(newValue.isPresent()) { - this.period = Period.parse((String)newValue.get()); - }else { - this.period = null; - } } if(nextDischargeDate != null && nextDischargeDate.isBefore(LocalDate.now())) { addPeriod(); @@ -216,9 +209,9 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } private void addPeriod() { - if(this.nextDischargeDate != null && this.period != null) { - this.nextDischargeDate.plus(period); - nextDischarge.updateValue(this.nextDischargeDate.toString(),true); + if (this.nextDischargeDate != null && dischargePeriod.isValuePresent()) { + this.nextDischargeDate = this.nextDischargeDate.plus(Period.parse(dischargePeriod.getValue())); + nextDischarge.updateValue(this.nextDischargeDate.toString(), true); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 84bda7418f3..818a89c672f 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -60,14 +60,13 @@ public AvoidTotalDischargeController(String thingId) { @ChannelInfo(title = "Max Soc", description = "If the System is full the charge is blocked untill the soc decrease below the maxSoc.", type = Long.class, defaultValue = "95") public final ConfigChannel maxSoc = new ConfigChannel("maxSoc", this); @ChannelInfo(title = "Next Discharge", description = "Next Time, the ess will discharge completely.", type = String.class,defaultValue = "2018-03-09") - public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); + public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class,defaultValue = "P4W") - public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); + public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge",this); private LocalDate nextDischargeDate; - private Period period; /* * Methods @@ -166,12 +165,6 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol }else { nextDischargeDate = null; } - }else if(this.dischargePeriod.equals(channel)) { - if(newValue.isPresent()) { - this.period = Period.parse((String)newValue.get()); - }else { - this.period = null; - } } if(nextDischargeDate != null && nextDischargeDate.isBefore(LocalDate.now())) { addPeriod(); @@ -179,9 +172,9 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } private void addPeriod() { - if(this.nextDischargeDate != null && this.period != null) { - this.nextDischargeDate.plus(period); - nextDischarge.updateValue(this.nextDischargeDate.toString(),true); + if (this.nextDischargeDate != null && dischargePeriod.isValuePresent()) { + this.nextDischargeDate = this.nextDischargeDate.plus(Period.parse(dischargePeriod.getValue())); + nextDischarge.updateValue(this.nextDischargeDate.toString(), true); } } From 0fba98f28abd10c030fada741c86a58dc33cda2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 19 Feb 2018 12:43:54 +0100 Subject: [PATCH 084/156] implement periodic discharge --- ...idTotalDischargeSocTimeLineController.java | 78 +++++-- .../avoidtotaldischargesoctimeline/Ess.java | 204 +++++++++--------- 2 files changed, 169 insertions(+), 113 deletions(-) diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java index 1e4a8b614e4..464a801d904 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java @@ -20,7 +20,10 @@ *******************************************************************************/ package io.openems.impl.controller.symmetric.avoidtotaldischargesoctimeline; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.Period; import java.time.format.DateTimeFormatter; import java.util.Optional; import java.util.Set; @@ -44,6 +47,7 @@ public class AvoidTotalDischargeSocTimeLineController extends Controller implements ChannelChangeListener { private ThingStateChannels thingState = new ThingStateChannels(this); + /* * Constructors */ @@ -64,6 +68,21 @@ public AvoidTotalDischargeSocTimeLineController(String thingId) { @ChannelInfo(title = "Soc timeline", description = "This option configures an minsoc at a time for an ess. If no minsoc for an ess is configured the controller uses the minsoc of the ess.", type = JsonArray.class) public final ConfigChannel socTimeline = new ConfigChannel("socTimeline", this) .addChangeListener(this); + @ChannelInfo(title = "Next Discharge", description = "Next Time, the ess will discharge completely.", type = String.class, defaultValue = "2018-03-09") + public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this) + .addChangeListener(this); + @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class, defaultValue = "P4W") + public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this) + .addChangeListener(this); + @ChannelInfo(title = "Discharge Start Time", description = "The time of the Day to start Discharging.", type = String.class, defaultValue = "12:00:00") + public final ConfigChannel dischargeTime = new ConfigChannel("dischargeTime", this) + .addChangeListener(this); + @ChannelInfo(title = "Enable Discharge", description = "This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type = Boolean.class, defaultValue = "true") + public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge", this); + + private LocalDate nextDischargeDate; + private LocalTime dischargeStartTime; + private LocalDateTime dischargeStart; /* * Methods @@ -73,24 +92,18 @@ public void run() { try { LocalTime time = LocalTime.now(); for (Ess ess : esss.value()) { + if(dischargeStart != null && dischargeStart.isBefore(LocalDateTime.now()) && ess.currentState != Ess.State.EMPTY ) { + ess.currentState = Ess.State.EMPTY; + } switch (ess.currentState) { case CHARGESOC: if (ess.soc.value() > ess.getMinSoc(time)) { ess.currentState = State.MINSOC; } else { try { - Optional currentMinValue = ess.setActivePower.writeMin(); - if (currentMinValue.isPresent() && currentMinValue.get() < 0) { - // Force Charge with minimum of MaxChargePower/5 - log.info("Force charge. Set ActivePower=Max[" + currentMinValue.get() / 5 + "]"); - ess.setActivePower.pushWriteMax(currentMinValue.get() / 5); - } else { - log.info("Avoid discharge. Set ActivePower=Max[-1000 W]"); - ess.setActivePower.pushWriteMax(-1000L); - } + ess.setActivePower.pushWriteMax(Math.abs(ess.maxNominalPower.value()) * -1); } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error("Failed to force Charge!", e); } } break; @@ -116,9 +129,23 @@ public void run() { ess.currentState = State.MINSOC; } break; + case EMPTY: + if (ess.allowedDischarge.value() == 0 || ess.soc.value() < 1) { + // Ess is Empty set Date and charge to minSoc + addPeriod(); + ess.currentState = State.CHARGESOC; + }else { + //Force discharge with max power + try { + ess.setActivePower.pushWriteMin(ess.setActivePower.writeMax().orElse(Math.abs(ess.maxNominalPower.value()))); + } catch (WriteChannelException e) { + log.error("Failed to force Discharge!", e); + } + } + break; } - } + } catch (InvalidValueException e) { log.error(e.getMessage()); } @@ -159,6 +186,33 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } } } + } else if (this.nextDischarge.equals(channel)) { + if (newValue.isPresent()) { + nextDischargeDate = LocalDate.parse((String) newValue.get()); + } else { + nextDischargeDate = null; + } + } else if(this.dischargeTime.equals(channel)) { + if (newValue.isPresent()) { + this.dischargeStartTime = LocalTime.parse((String) newValue.get()); + } else { + this.dischargeStartTime = null; + } + } + if (nextDischargeDate != null && nextDischargeDate.isBefore(LocalDate.now())) { + addPeriod(); + } + if(nextDischargeDate != null && dischargeStartTime != null) { + dischargeStart = nextDischargeDate.atTime(dischargeStartTime); + }else { + dischargeStart = null; + } + } + + private void addPeriod() { + if (this.nextDischargeDate != null && dischargePeriod.isValuePresent()) { + this.nextDischargeDate = this.nextDischargeDate.plus(Period.parse(dischargePeriod.getValue())); + nextDischarge.updateValue(this.nextDischargeDate.toString(), true); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java index e06ab42d434..9ec7c3825c9 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java @@ -1,101 +1,103 @@ -/******************************************************************************* - * 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.symmetric.avoidtotaldischargesoctimeline; - -import java.time.LocalTime; -import java.util.Map; -import java.util.TreeMap; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.SymmetricEssNature; -import io.openems.api.exception.InvalidValueException; - -@IsThingMap(type = SymmetricEssNature.class) -public class Ess extends ThingMap { - - public final ReadChannel minSoc; - public final WriteChannel setActivePower; - public final ReadChannel soc; - public final ReadChannel systemState; - public int maxPowerPercent = 100; - public final ReadChannel allowedDischarge; - public final ReadChannel chargeSoc; - private TreeMap timeline = new TreeMap<>(); - public State currentState = State.NORMAL; - - public enum State { - NORMAL, MINSOC, CHARGESOC - } - - public Ess(SymmetricEssNature ess) { - super(ess); - setActivePower = ess.setActivePower().required(); - systemState = ess.systemState().required(); - soc = ess.soc().required(); - minSoc = ess.minSoc().required(); - allowedDischarge = ess.allowedDischarge().required(); - chargeSoc = ess.chargeSoc().required(); - } - - public void addTime(LocalTime time, int minSoc, int chargeSoc) { - Soc soc = new Soc(minSoc, chargeSoc); - timeline.put(time, soc); - } - - public int getMinSoc(LocalTime time) throws InvalidValueException { - Map.Entry entry = timeline.floorEntry(time); - if (entry != null) { - return entry.getValue().minSoc; - } - entry = timeline.lastEntry(); - if (entry != null) { - return entry.getValue().minSoc; - } - return minSoc.value(); - } - - public int getChargeSoc(LocalTime time) throws InvalidValueException { - Map.Entry entry = timeline.floorEntry(time); - if (entry != null) { - return entry.getValue().chargeSoc; - } - entry = timeline.lastEntry(); - if (entry != null) { - return entry.getValue().chargeSoc; - } - return chargeSoc.value(); - } - - private class Soc { - public final int minSoc; - public final int chargeSoc; - - public Soc(int minSoc, int chargeSoc) { - super(); - this.minSoc = minSoc; - this.chargeSoc = chargeSoc; - } - - } -} +/******************************************************************************* + * 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.symmetric.avoidtotaldischargesoctimeline; + +import java.time.LocalTime; +import java.util.Map; +import java.util.TreeMap; + +import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.controller.IsThingMap; +import io.openems.api.controller.ThingMap; +import io.openems.api.device.nature.ess.SymmetricEssNature; +import io.openems.api.exception.InvalidValueException; + +@IsThingMap(type = SymmetricEssNature.class) +public class Ess extends ThingMap { + + public final ReadChannel minSoc; + public final WriteChannel setActivePower; + public final ReadChannel soc; + public final ReadChannel systemState; + public int maxPowerPercent = 100; + public final ReadChannel allowedDischarge; + public final ReadChannel chargeSoc; + private TreeMap timeline = new TreeMap<>(); + public State currentState = State.NORMAL; + public final ReadChannel maxNominalPower; + + public enum State { + NORMAL, MINSOC, CHARGESOC, EMPTY + } + + public Ess(SymmetricEssNature ess) { + super(ess); + setActivePower = ess.setActivePower().required(); + systemState = ess.systemState().required(); + soc = ess.soc().required(); + minSoc = ess.minSoc().required(); + allowedDischarge = ess.allowedDischarge().required(); + chargeSoc = ess.chargeSoc().required(); + maxNominalPower = ess.maxNominalPower(); + } + + public void addTime(LocalTime time, int minSoc, int chargeSoc) { + Soc soc = new Soc(minSoc, chargeSoc); + timeline.put(time, soc); + } + + public int getMinSoc(LocalTime time) throws InvalidValueException { + Map.Entry entry = timeline.floorEntry(time); + if (entry != null) { + return entry.getValue().minSoc; + } + entry = timeline.lastEntry(); + if (entry != null) { + return entry.getValue().minSoc; + } + return minSoc.value(); + } + + public int getChargeSoc(LocalTime time) throws InvalidValueException { + Map.Entry entry = timeline.floorEntry(time); + if (entry != null) { + return entry.getValue().chargeSoc; + } + entry = timeline.lastEntry(); + if (entry != null) { + return entry.getValue().chargeSoc; + } + return chargeSoc.value(); + } + + private class Soc { + public final int minSoc; + public final int chargeSoc; + + public Soc(int minSoc, int chargeSoc) { + super(); + this.minSoc = minSoc; + this.chargeSoc = chargeSoc; + } + + } +} From b25c7bda94e714ade7a994920a3f24c26ecae542 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Mon, 19 Feb 2018 19:40:05 +0100 Subject: [PATCH 085/156] Simplify code; add debouncing; write more fields to Odoo --- .../impl/provider/EdgeWebsocketServer.java | 70 ++++++++----------- io.openems.backend.metadata.api/bnd.bnd | 5 +- .../io/openems/backend/metadata/api/Edge.java | 50 +++++++++++-- .../backend/metadata/api/OnSetInteger.java | 5 ++ ...{OnSetConfig.java => OnSetJsonObject.java} | 2 +- .../backend/metadata/api/OnSetString.java | 5 ++ ...stMessage.java => OnSetZonedDateTime.java} | 2 +- .../openems/backend/metadata/odoo/Field.java | 5 +- .../openems/backend/metadata/odoo/Odoo.java | 70 +++++++++++++------ .../io/openems/common/utils/JsonUtils.java | 10 ++- 10 files changed, 150 insertions(+), 74 deletions(-) create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetInteger.java rename io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/{OnSetConfig.java => OnSetJsonObject.java} (77%) create mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetString.java rename io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/{OnSetLastMessage.java => OnSetZonedDateTime.java} (76%) diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index febc04ff4bc..d7355953f02 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -2,6 +2,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import org.java_websocket.WebSocket; @@ -11,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.backend.common.events.BackendEventConstants; @@ -216,49 +218,33 @@ private void timedata(int[] edgeIds, JsonObject jTimedata) { * set last update timestamps in MetadataService */ edge.setLastMessage(); + + for (Entry jTimedataEntry : jTimedata.entrySet()) { + try { + JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue()); + // set Odoo last update timestamp only for those channels + for (String channel : jChannels.keySet()) { + if (channel.endsWith("ActivePower") + || channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2") + | channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) { + edge.setLastUpdate(); + } + } + + // set specific Odoo values + if (jChannels.has("ess0/Soc")) { + int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt(); + edge.setSoc(soc); + } + if (jChannels.has("system0/PrimaryIpAddress")) { + String ipv4 = JsonUtils.getAsPrimitive(jChannels, "system0/PrimaryIpAddress").getAsString(); + edge.setIpv4(ipv4); + } + } catch (OpenemsException e) { + log.error("Edgde [" + edge.getName() + "] error: " + e.getMessage()); + } + } } } - - // TODO - // try { - // for (Entry jTimedataEntry : jTimedata.entrySet()) { - // try { - // JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue()); - // - // // set Odoo last update timestamp only for those channels - // for (String channel : jChannels.keySet()) { - // if (channel.endsWith("ActivePower") - // || channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2") - // | channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) { - // for (MetadataDevice device : devices) { - // device.setLastUpdate(); - // } - // } - // } - // - // // set specific Odoo values - // if (jChannels.has("ess0/Soc")) { - // int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt(); - // for (MetadataDevice device : devices) { - // device.setSoc(soc); - // } - // } - // if (jChannels.has("system0/PrimaryIpAddress")) { - // String ipv4 = JsonUtils.getAsPrimitive(jChannels, - // "system0/PrimaryIpAddress").getAsString(); - // for (MetadataDevice device : devices) { - // device.setIpV4(ipv4); - // } - // } - // } catch (OpenemsException e) { - // log.error("Device [" + String.join(",", devices.getNames()) + "] error: " + - // e.getMessage()); - // } - // } - // - // } catch (OpenemsException e) { - // log.error("Device [" + devices.getNamesString() + "] error: " + - // e.getMessage()); - // } } } diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd index 1079424577d..df1c13eabf6 100644 --- a/io.openems.backend.metadata.api/bnd.bnd +++ b/io.openems.backend.metadata.api/bnd.bnd @@ -5,8 +5,7 @@ Bundle-Version: 1.0.0.${tstamp} Bundle-Description: Read and store Metadata -Export-Package: \ - io.openems.backend.metadata.api +Export-Package: io.openems.backend.metadata.api Require-Capability: \ compile-only @@ -24,4 +23,4 @@ Require-Capability: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 --runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' 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 81ec506fd9f..dbc832fea29 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 @@ -13,6 +13,9 @@ public class Edge { private String producttype; private JsonObject jConfig; private ZonedDateTime lastMessage = null; + private ZonedDateTime lastUpdate = null; + private Integer soc = null; + private String ipv4 = null; private boolean isOnline; public Edge(int id, String name, String comment, String producttype, JsonObject jConfig) { @@ -38,9 +41,9 @@ public void setOnline(boolean isOnline) { // TODO call OnSetOnline } - private Optional onSetConfig = Optional.empty(); + private Optional onSetConfig = Optional.empty(); - public void onSetConfig(OnSetConfig listener) { + public void onSetConfig(OnSetJsonObject listener) { this.onSetConfig = Optional.of(listener); } @@ -75,9 +78,9 @@ public String toString() { + ", isOnline=" + isOnline + "]"; } - private Optional onSetLastMessage = Optional.empty(); + private Optional onSetLastMessage = Optional.empty(); - public void onSetLastMessage(OnSetLastMessage listener) { + public void onSetLastMessage(OnSetZonedDateTime listener) { this.onSetLastMessage = Optional.of(listener); } @@ -87,4 +90,43 @@ public void setLastMessage() { this.onSetLastMessage.get().call(this.lastMessage); } } + + private Optional onSetLastUpdate = Optional.empty(); + + public void onSetLastUpdate(OnSetZonedDateTime listener) { + this.onSetLastUpdate = Optional.of(listener); + } + + public void setLastUpdate() { + this.lastUpdate = ZonedDateTime.now(ZoneOffset.UTC); + if (this.onSetLastUpdate.isPresent()) { + this.onSetLastUpdate.get().call(this.lastUpdate); + } + } + + private Optional onSetSoc = Optional.empty(); + + public void onSetSoc(OnSetInteger listener) { + this.onSetSoc = Optional.of(listener); + } + + public void setSoc(int soc) { + this.soc = soc; + if (this.onSetSoc.isPresent()) { + this.onSetSoc.get().call(this.soc); + } + } + + private Optional onSetIpv4 = Optional.empty(); + + public void onSetIpv4(OnSetString listener) { + this.onSetIpv4 = Optional.of(listener); + } + + public void setIpv4(String ipv4) { + this.ipv4 = ipv4; + if (this.onSetIpv4.isPresent()) { + this.onSetIpv4.get().call(this.ipv4); + } + } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetInteger.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetInteger.java new file mode 100644 index 00000000000..6a478d5d0fe --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetInteger.java @@ -0,0 +1,5 @@ +package io.openems.backend.metadata.api; + +public interface OnSetInteger { + public void call(int soc); +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetJsonObject.java similarity index 77% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetJsonObject.java index 8ff0b4c9ed5..743daf0f5f9 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetConfig.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetJsonObject.java @@ -2,6 +2,6 @@ import com.google.gson.JsonObject; -public interface OnSetConfig { +public interface OnSetJsonObject { public void call(JsonObject config); } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetString.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetString.java new file mode 100644 index 00000000000..9842ef91ce6 --- /dev/null +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetString.java @@ -0,0 +1,5 @@ +package io.openems.backend.metadata.api; + +public interface OnSetString { + public void call(String ipv4); +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetZonedDateTime.java similarity index 76% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java rename to io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetZonedDateTime.java index ee60c1d0abd..ad9754124d7 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetLastMessage.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OnSetZonedDateTime.java @@ -2,6 +2,6 @@ import java.time.ZonedDateTime; -public interface OnSetLastMessage { +public interface OnSetZonedDateTime { public void call(ZonedDateTime lastMessage); } diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java index 4723ef9ebae..6ea065e2401 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Field.java @@ -9,7 +9,10 @@ public enum FemsDevice implements Field { COMMENT("comment"), // PRODUCT_TYPE("producttype"), // OPENEMS_CONFIG("openems_config"), // - LAST_MESSAGE("lastmessage"); + LAST_MESSAGE("lastmessage"), // + LAST_UPDATE("lastupdate"), // + SOC("soc"), // + IPV4("ipv4"); private final String n; diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index a8859506f96..c144128461d 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -8,6 +8,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; +import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -213,29 +214,27 @@ private Optional getEdgeForceRefresh(int edgeId) { JsonUtils.getAsJsonObject( JsonUtils.parse((String) edgeMap.get(Field.FemsDevice.OPENEMS_CONFIG.n())))); edge.onSetConfig(jConfig -> { - /* - * Update Edge config in Odoo - */ - try { - String config = new GsonBuilder().setPrettyPrinting().create().toJson(jConfig); - OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edgeId, - new FieldValue(Field.FemsDevice.OPENEMS_CONFIG, config)); - log.info("Updated Edge config [" + edge.getName() + "]"); - } catch (OpenemsException e) { - log.error("Unable to update Edge [ID:" + edge.getName() + "] config: " + e.getMessage()); - } + // Update Edge config in Odoo + String config = new GsonBuilder().setPrettyPrinting().create().toJson(jConfig); + this.write(edge, new FieldValue(Field.FemsDevice.OPENEMS_CONFIG, config), false); }); edge.onSetLastMessage(time -> { - /* - * Set LastMessage timestamp in Odoo - */ - // TODO only fire once per minute - try { - OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edge.getId(), - new FieldValue(Field.FemsDevice.LAST_MESSAGE, OdooUtils.DATETIME_FORMATTER.format(time))); - } catch (OpenemsException e) { - log.error("Unable to update Edge [ID:" + edge.getName() + "] lastMessage: " + e.getMessage()); - } + // Set LastMessage timestamp in Odoo + this.write(edge, + new FieldValue(Field.FemsDevice.LAST_MESSAGE, OdooUtils.DATETIME_FORMATTER.format(time)), true); + }); + edge.onSetLastUpdate(time -> { + // Set LastUpdate timestamp in Odoo + this.write(edge, + new FieldValue(Field.FemsDevice.LAST_UPDATE, OdooUtils.DATETIME_FORMATTER.format(time)), true); + }); + edge.onSetSoc(soc -> { + // Set SoC in Odoo + this.write(edge, new FieldValue(Field.FemsDevice.SOC, String.valueOf(soc)), true); + }); + edge.onSetIpv4(ipv4 -> { + // Set IPv4 in Odoo + this.write(edge, new FieldValue(Field.FemsDevice.IPV4, String.valueOf(ipv4)), true); }); edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); // store in cache @@ -249,6 +248,35 @@ private Optional getEdgeForceRefresh(int edgeId) { } } + private final int DEBOUNCE_SECONDS = 60; + + private HashMap lastWriteMap = new HashMap<>(); + + private void write(Edge edge, FieldValue fieldValue, boolean debounce) { + Instant now = Instant.now(); + boolean executeWrite = true; + if (debounce) { + // debounce = avoid writing too often + synchronized (this.lastWriteMap) { + Instant lastWrite = lastWriteMap.get(fieldValue.getField().n()); + if (lastWrite != null && now.minusSeconds(DEBOUNCE_SECONDS).isBefore(lastWrite)) { + executeWrite = false; + } else { + this.lastWriteMap.put(fieldValue.getField().n(), now); + } + } + } + if (executeWrite) { + try { + OdooUtils.write(this.url, this.database, this.uid, this.password, "fems.device", edge.getId(), + fieldValue); + } catch (OpenemsException e) { + log.error("Unable to update Edge [ID:" + edge.getName() + "] field [" + fieldValue.getField().n() + + "] : " + e.getMessage()); + } + } + } + // public Optional getUser(int id) { // return Optional.ofNullable(this.users.get(id)); // } 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 1e58382ee91..2f5b4fbed86 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -47,7 +47,7 @@ public static Optional getAsOptionalJsonArray(JsonElement jElement, S return Optional.empty(); } } - + public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsException { if (!jElement.isJsonObject()) { throw new OpenemsException("This is not a JsonObject: " + jElement); @@ -55,6 +55,14 @@ public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsExc return jElement.getAsJsonObject(); }; + public static Optional getAsOptionalJsonObject(JsonElement jElement) { + try { + return Optional.of(getAsJsonObject(jElement)); + } catch (OpenemsException e) { + return Optional.empty(); + } + }; + public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws OpenemsException { JsonElement jsubElement = getSubElement(jElement, memberName); if (!jsubElement.isJsonObject()) { From 88c8d21b59f13283842ae10cb4a355213a618dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 20 Feb 2018 10:59:17 +0100 Subject: [PATCH 086/156] change power calculation to double to avoid 1W offset --- .../core/utilities/power/SymmetricPowerImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java index b91baa6fe0f..f1f09c99f4b 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java @@ -24,8 +24,8 @@ public class SymmetricPowerImpl extends SymmetricPower implements LimitationChan private List staticLimitations; private List dynamicLimitations; - private long lastActivePower = 0; - private long lastReactivePower = 0; + private double lastActivePower = 0; + private double lastReactivePower = 0; public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePower, WriteChannel setReactivePower, Bridge bridge) { @@ -77,13 +77,13 @@ private void writePower() { Point p = reduceToZero(); Coordinate c = p.getCoordinate(); setGeometry(p); - long activePowerDelta = (long) (c.x - lastActivePower); - long reactivePowerDelta = (long) (c.y - lastReactivePower); + double activePowerDelta = c.x - lastActivePower; + double reactivePowerDelta = c.y - lastReactivePower; lastActivePower += activePowerDelta/2; lastReactivePower += reactivePowerDelta/2; try { - this.setActivePower.pushWrite(lastActivePower); - this.setReactivePower.pushWrite(lastReactivePower); + this.setActivePower.pushWrite((long) lastActivePower); + this.setReactivePower.pushWrite((long) lastReactivePower); setActivePower.shadowCopyAndReset(); setReactivePower.shadowCopyAndReset(); } catch (WriteChannelException e) { From fccaea4fe89710cdeaa76bc046bfc8e88316d88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 20 Feb 2018 10:59:33 +0100 Subject: [PATCH 087/156] Add DoubleArray --- edge/src/io/openems/core/utilities/BitUtils.java | 1 + edge/src/io/openems/core/utilities/OpenemsTypes.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/edge/src/io/openems/core/utilities/BitUtils.java b/edge/src/io/openems/core/utilities/BitUtils.java index ac662f1eba2..07a0bb6985e 100644 --- a/edge/src/io/openems/core/utilities/BitUtils.java +++ b/edge/src/io/openems/core/utilities/BitUtils.java @@ -34,6 +34,7 @@ public static int getBitLength(Class type) throws NotImplementedException { case INET_4_ADDRESS: // TODO case STRING: case LONG_ARRAY: + case DOUBLE_ARRAY: case JSON_ARRAY: case JSON_OBJECT: case DEVICE_NATURE: diff --git a/edge/src/io/openems/core/utilities/OpenemsTypes.java b/edge/src/io/openems/core/utilities/OpenemsTypes.java index eea06aa8ab2..97dc08ebdcc 100644 --- a/edge/src/io/openems/core/utilities/OpenemsTypes.java +++ b/edge/src/io/openems/core/utilities/OpenemsTypes.java @@ -25,7 +25,7 @@ public enum OpenemsTypes { /* * Arrays of primitives */ - LONG_ARRAY, + LONG_ARRAY,DOUBLE_ARRAY, /* * Complex types */ @@ -65,6 +65,8 @@ public static OpenemsTypes get(Class type) throws NotImplementedException { } else if (Long[].class.isAssignableFrom(type)) { return LONG_ARRAY; + } else if (Double[].class.isAssignableFrom(type)) { + return DOUBLE_ARRAY; } else if (Inet4Address.class.isAssignableFrom(type)) { return INET_4_ADDRESS; From 276d56d9f7bfe54e8e66e13e6e7a86594cc93306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 20 Feb 2018 11:00:22 +0100 Subject: [PATCH 088/156] Fix limitation won't apply if limit is equal maxApparentPower --- .../core/utilities/power/PGreaterEqualLimitation.java | 6 +++--- .../core/utilities/power/PSmallerEqualLimitation.java | 6 +++--- .../core/utilities/power/QGreaterEqualLimitation.java | 6 +++--- .../core/utilities/power/QSmallerEqualLimitation.java | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java b/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java index ac2b94f8628..cddb311cea9 100644 --- a/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java +++ b/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java @@ -16,9 +16,9 @@ public void setP(Long p) { if (p != this.p) { if (p != null) { long pMin = p; - long pMax = power.getMaxApparentPower(); - long qMin = power.getMaxApparentPower() * -1; - long qMax = power.getMaxApparentPower(); + long pMax = power.getMaxApparentPower()+1; + long qMin = power.getMaxApparentPower() * -1-1; + long qMax = power.getMaxApparentPower()+1; Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); diff --git a/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java b/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java index f735c7e21d3..13de5aada04 100644 --- a/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java +++ b/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java @@ -15,10 +15,10 @@ public PSmallerEqualLimitation(SymmetricPower power) { public void setP(Long p) { if (p != this.p) { if (p != null) { - long pMin = power.getMaxApparentPower() * -1; + long pMin = power.getMaxApparentPower() * -1-1; long pMax = p; - long qMin = power.getMaxApparentPower() * -1; - long qMax = power.getMaxApparentPower(); + long qMin = power.getMaxApparentPower() * -1-1; + long qMax = power.getMaxApparentPower()+1; Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); diff --git a/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java b/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java index 84af26b58fa..937bd1128c0 100644 --- a/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java +++ b/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java @@ -15,10 +15,10 @@ public QGreaterEqualLimitation(SymmetricPower power) { public void setQ(Long q) { if (q != this.q) { if (q != null) { - long pMin = power.getMaxApparentPower() * -1; - long pMax = power.getMaxApparentPower(); + long pMin = power.getMaxApparentPower() * -1-1; + long pMax = power.getMaxApparentPower()+1; long qMin = q; - long qMax = power.getMaxApparentPower(); + long qMax = power.getMaxApparentPower()+1; Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; rect = SymmetricPowerImpl.getFactory().createPolygon(coordinates); diff --git a/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java b/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java index fb868b3a9d9..a3e2dc29e19 100644 --- a/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java +++ b/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java @@ -15,9 +15,9 @@ public QSmallerEqualLimitation(SymmetricPower power) { public void setQ(Long q) { if (q != this.q) { if (q != null) { - long pMin = power.getMaxApparentPower() * -1; - long pMax = power.getMaxApparentPower(); - long qMin = power.getMaxApparentPower() * -1; + long pMin = power.getMaxApparentPower() * -1-1; + long pMax = power.getMaxApparentPower()+1; + long qMin = power.getMaxApparentPower() * -1-1; long qMax = q; Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin), new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) }; From 54320d4e6a28f404f90bda7ea6a5625e02812669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 20 Feb 2018 11:08:30 +0100 Subject: [PATCH 089/156] disable discharge by default --- .../AvoidTotalDischargeController.java | 4 ++-- .../AvoidTotalDischargeController.java | 7 ++++--- .../AvoidTotalDischargeSocTimeLineController.java | 10 ++++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index b4972f214a9..1558ff4d222 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -63,8 +63,8 @@ public AvoidTotalDischargeController(String id) { public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class,defaultValue = "P4W") public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); - @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") - public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge",this); + @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="false") + public final ConfigChannel enableDischarge = new ConfigChannel("enableDischarge",this); private LocalDate nextDischargeDate; diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index d744331e942..c559a429960 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -63,8 +63,8 @@ public AvoidTotalDischargeController(String thingId) { public final ConfigChannel nextDischarge = new ConfigChannel("nextDischarge", this).addChangeListener(this); @ChannelInfo(title = "Discharge Period", description = "The Period of time between two Discharges.https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-", type = String.class,defaultValue = "P4W") public final ConfigChannel dischargePeriod = new ConfigChannel("dischargePeriod", this).addChangeListener(this); - @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="true") - public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge",this); + @ChannelInfo(title = "Enable Discharge", description="This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type=Boolean.class,defaultValue="false") + public final ConfigChannel enableDischarge = new ConfigChannel("enableDischarge",this); private LocalDate nextDischargeDate; @@ -86,7 +86,8 @@ public void run() { ess.currentState = State.MINSOC; } else { try { - ess.maxActivePowerLimit.setP(ess.maxNominalPower.valueOptional().orElse(-1000L)); + long maxChargePower = Math.max(ess.power.getMinP().orElse(0L),Math.abs(ess.maxNominalPower.valueOptional().orElse(10000L)/5)*-1); + ess.maxActivePowerLimit.setP(maxChargePower); ess.power.applyLimitation(ess.maxActivePowerLimit); } catch (PowerException e) { log.error("Failed to set Power!",e); diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java index c465dc0d9b7..6d6df0aed4c 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java @@ -76,8 +76,8 @@ public AvoidTotalDischargeSocTimeLineController(String thingId) { @ChannelInfo(title = "Discharge Start Time", description = "The time of the Day to start Discharging.", type = String.class, defaultValue = "12:00:00") public final ConfigChannel dischargeTime = new ConfigChannel("dischargeTime", this) .addChangeListener(this); - @ChannelInfo(title = "Enable Discharge", description = "This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type = Boolean.class, defaultValue = "true") - public final ConfigChannel enableDischarge = new ConfigChannel("EnableDischarge", this); + @ChannelInfo(title = "Enable Discharge", description = "This option allowes the system to discharge the ess according to the nextDischarge completely. This improves the soc calculation.", type = Boolean.class, defaultValue = "false") + public final ConfigChannel enableDischarge = new ConfigChannel("enableDischarge", this); private LocalDate nextDischargeDate; private LocalTime dischargeStartTime; @@ -100,7 +100,8 @@ public void run() { ess.currentState = State.MINSOC; } else { try { - ess.maxActivePowerLimit.setP(Math.abs(ess.maxNominalPower.valueOptional().orElse(1000L))*-1); + long maxChargePower = Math.max(ess.power.getMinP().orElse(0L),Math.abs(ess.maxNominalPower.valueOptional().orElse(1000L))*-1); + ess.maxActivePowerLimit.setP(maxChargePower); ess.power.applyLimitation(ess.maxActivePowerLimit); } catch (PowerException e) { log.error("Failed to set Power!",e); @@ -134,7 +135,8 @@ public void run() { }else { //Force discharge with max power try { - ess.minActivePowerLimit.setP(Math.abs(ess.maxNominalPower.value())); + long maxDischargePower = Math.min(Math.abs(ess.maxNominalPower.value()),ess.power.getMaxP().orElse(0L)); + ess.minActivePowerLimit.setP(maxDischargePower); ess.power.applyLimitation(ess.minActivePowerLimit); } catch (PowerException e) { log.error("Failed to force Discharge!", e); From 12741f4bdaa07e506d8e8d3f104f7b74631a7e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 20 Feb 2018 14:33:06 +0100 Subject: [PATCH 090/156] call reset direct after write --- .../io/openems/core/utilities/power/SymmetricPowerImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java index f1f09c99f4b..37692ed73ef 100644 --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java +++ b/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java @@ -121,7 +121,6 @@ public void onLimitationChange(Limitation sender) { public void onBridgeChange(BridgeEvent event) { switch (event.getPosition()) { case BEFOREREADOTHER1: - this.reset(); break; case BEFOREREADOTHER2: break; @@ -129,7 +128,7 @@ public void onBridgeChange(BridgeEvent event) { break; case BEFOREWRITE: this.writePower(); - + this.reset(); break; default: break; From cb4ce81e369a2ade04230dd48a607b2821c98293 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 22 Feb 2018 17:26:05 +0100 Subject: [PATCH 091/156] Add custom InfluxDB submodule --- .gitmodules | 0 io.openems.wrapper/{websocket.bnd => influxdb.bnd} | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .gitmodules rename io.openems.wrapper/{websocket.bnd => influxdb.bnd} (100%) diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.wrapper/websocket.bnd b/io.openems.wrapper/influxdb.bnd similarity index 100% rename from io.openems.wrapper/websocket.bnd rename to io.openems.wrapper/influxdb.bnd From 2292bd80c89a4b369a6a9321cff79449988b2f8a Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 22 Feb 2018 21:39:18 +0100 Subject: [PATCH 092/156] Working dependency chain for InfluxDB --- cnf/build.bnd | 2 +- cnf/central.xml | 56 +-- .../BackendApp.bndrun | 9 +- .../bnd.bnd | 4 +- .../bnd.bnd | 9 +- .../io.openems.backend.timedata.influx.bndrun | 27 +- .../backend/timedata/influx/Influx.java | 354 ++++++++-------- .../timedata/influx/InfluxdbUtils.java | 381 ++++++++++-------- io.openems.common/bnd.bnd | 4 +- io.openems.wrapper/influxdb.bnd | 19 - 10 files changed, 450 insertions(+), 415 deletions(-) delete mode 100644 io.openems.wrapper/influxdb.bnd diff --git a/cnf/build.bnd b/cnf/build.bnd index 5ea4bbbf5ef..f398d40eb8c 100644 --- a/cnf/build.bnd +++ b/cnf/build.bnd @@ -1,3 +1,3 @@ # # This file is left open for you to define your macros -# +# \ No newline at end of file diff --git a/cnf/central.xml b/cnf/central.xml index 4cc7913e395..3b62d3b8cec 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -9,6 +9,31 @@ pom + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.influxdb-java + 2.3_1 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okhttp + 2.7.5_1 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okio + 1.13.0_1 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.retrofit + 1.9.0_1 + + ch.qos.logback logback-classic @@ -36,31 +61,6 @@ org.apache.felix.gogo.shell 1.0.0 - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.commons-httpclient - 3.1_7 - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.influxdb-java - 2.8_1 - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.okhttp - 3.2.0_1 - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.okio - 1.13.0_1 - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.retrofit - 2.3.0_1 - org.apache.servicemix.bundles org.apache.servicemix.bundles.xmlrpc-client @@ -71,6 +71,11 @@ org.apache.servicemix.bundles.ws-commons-util 1.0.2_2 + + org.java-websocket + Java-WebSocket + 1.3.7 + org.slf4j slf4j-api @@ -92,5 +97,4 @@ 1.8.3 - diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index bf27a20d85b..af8f1a1f49b 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -11,7 +11,6 @@ JPM-Command: openems-backend osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-service)',\ osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-api)' - -runproperties: \ felix.cm.dir=C:/openems-config,\ org.ops4j.pax.logging.service.frameworkEventsLogLevel="DISABLED" @@ -30,7 +29,6 @@ JPM-Command: openems-backend org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ io.openems.wrapper.websocket;version=snapshot,\ com.google.gson;version='[2.8.2,2.8.3)',\ - com.google.guava;version='[19.0.0,19.0.1)',\ io.openems.common;version=snapshot,\ io.openems.backend.metadata.odoo.provider;version=snapshot,\ io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ @@ -40,7 +38,8 @@ JPM-Command: openems-backend org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ org.apache.servicemix.bundles.xmlrpc-client;version='[3.1.3,3.1.4)',\ io.openems.backend.timedata.influx.provider;version=snapshot,\ - org.apache.servicemix.bundles.influxdb-java;version='[2.8.0,2.8.1)',\ - org.apache.servicemix.bundles.okhttp;version='[3.2.0,3.2.1)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ - org.apache.servicemix.bundles.retrofit;version='[2.3.0,2.3.1)' \ No newline at end of file + org.apache.servicemix.bundles.influxdb-java;version='[2.3.0,2.3.1)',\ + org.apache.servicemix.bundles.okhttp;version='[2.7.5,2.7.6)',\ + org.apache.servicemix.bundles.retrofit;version='[1.9.0,1.9.1)' \ No newline at end of file diff --git a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd index 59f2a976fea..8452b58f346 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd @@ -13,13 +13,13 @@ Export-Package: io.openems.backend.edgewebsocket.api osgi.enroute.base.api;version=2.1,\ io.openems.backend.edgewebsocket.api;version=latest,\ io.openems.wrapper.websocket;version=latest,\ - com.google.gson,\ com.google.guava,\ io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ io.openems.backend.common;version=latest,\ io.openems.backend.uiwebsocket.api;version=latest,\ - io.openems.backend.timedata.api;version=latest + io.openems.backend.timedata.api;version=latest,\ + com.google.gson;version=2.8 -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.timedata.influx.provider/bnd.bnd b/io.openems.backend.timedata.influx.provider/bnd.bnd index 767f0b75052..2e521b769e4 100644 --- a/io.openems.backend.timedata.influx.provider/bnd.bnd +++ b/io.openems.backend.timedata.influx.provider/bnd.bnd @@ -15,13 +15,14 @@ Private-Package: \ io.openems.common;version=latest,\ io.openems.backend.timedata.api;version=latest,\ io.openems.backend.common;version=latest,\ - com.google.gson,\ io.openems.backend.metadata.api;version=latest,\ - com.google.guava,\ - org.apache.servicemix.bundles.influxdb-java + org.apache.servicemix.bundles.influxdb-java,\ + com.google.gson;version=2.8,\ + com.google.guava;version=19.0 -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ osgi.enroute.hamcrest.wrapper;version=1.3 -Export-Package: io.openems.backend.timedata.api;-provide=true \ No newline at end of file +Export-Package: io.openems.backend.timedata.api;-provide=true +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' \ No newline at end of file diff --git a/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun b/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun index 0c09a42456a..19406cddf17 100644 --- a/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun +++ b/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun @@ -8,7 +8,28 @@ Bundle-SymbolicName: io.openems.backend.timedata.influx.launch JPM-Command: provider --runrequires: \ - osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx.provider)' +-runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx.provider)' --runbundles: ${error;You must first resolve this bndrun file before you can run it} +-runbundles: \ + com.google.gson;version='[2.8.2,2.8.3)',\ + io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ + io.openems.backend.metadata.odoo.provider;version=snapshot,\ + io.openems.backend.timedata.influx.provider;version=snapshot,\ + io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.apache.servicemix.bundles.ws-commons-util;version='[1.0.2,1.0.3)',\ + org.apache.servicemix.bundles.xmlrpc-client;version='[3.1.3,3.1.4)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + 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)',\ + slf4j.api;version='[1.8.0,1.8.1)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + org.apache.servicemix.bundles.influxdb-java;version='[2.3.0,2.3.1)',\ + org.apache.servicemix.bundles.okhttp;version='[2.2.0,2.2.1)',\ + org.apache.servicemix.bundles.okio;version='[1.2.0,1.2.1)',\ + org.apache.servicemix.bundles.retrofit;version='[1.9.0,1.9.1)' diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index 812d326e7b6..da6f114da39 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -1,10 +1,13 @@ package io.openems.backend.timedata.influx; +import java.text.NumberFormat; +import java.text.ParseException; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.TimeUnit; import org.influxdb.InfluxDB; @@ -16,6 +19,9 @@ 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.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; @@ -23,12 +29,16 @@ import com.google.common.collect.TreeBasedTable; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; -import io.openems.backend.metadata.api.OLD_MetadataDevice; +import io.openems.backend.metadata.api.Edge; +import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; @Designate(ocd = Influx.Config.class, factory = false) @Component(name = "InfluxDB", configurationPolicy = ConfigurationPolicy.REQUIRE) @@ -38,6 +48,9 @@ public class Influx implements TimedataService { private final String TMP_MINI_MEASUREMENT = "minies"; + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected volatile MetadataService metadataService; + @ObjectClassDefinition @interface Config { String database(); @@ -60,7 +73,7 @@ public class Influx implements TimedataService { private String password; private String measurement; - private InfluxDB influxDB; + private Optional influxDbOpt = Optional.empty(); private final Map deviceCacheMap = new HashMap<>(); @@ -75,33 +88,34 @@ void activate(Config config) throws OpenemsException { this.username = config.username(); this.password = config.password(); this.measurement = config.measurement(); - // TODO: connect asynchronously to not block the activator - // try { - // this.connect(); - // } catch (Exception e) { - // throw new OpenemsException("Connecting to InfluxDB failed: " + - // e.getMessage()); - // } + this.getInfluxDbConnection(); } @Deactivate void deactivate() { log.debug("Deactivate InfluxDB"); + if (this.influxDbOpt.isPresent()) { +// this.influxDbOpt.get().close(); + //TODO + } } - private void connect() throws Exception { - this.influxDB = InfluxDBFactory.connect("http://" + url + ":" + port, username, password); - try { - influxDB.ping(); - } catch (RuntimeException e) { - System.out.println("Unable to connect to InfluxDB: " + e.getMessage()); - // TODO log.error("Unable to connect to InfluxDB: " + e.getMessage()); - throw new Exception(e.getMessage()); + private synchronized InfluxDB getInfluxDbConnection() throws OpenemsException { + if (!this.influxDbOpt.isPresent()) { + InfluxDB influxDB = InfluxDBFactory.connect("http://" + url + ":" + port, username, password); + try { + influxDB.ping(); + this.influxDbOpt = Optional.of(influxDB); + } catch (RuntimeException e) { + throw new OpenemsException("Unable to connect to InfluxDB: " + e.getMessage()); + } } + return this.influxDbOpt.get(); } - // - private void writeData(int deviceId, TreeBasedTable data) { + private void writeData(int deviceId, TreeBasedTable data) throws OpenemsException { + InfluxDB influxDB = this.getInfluxDbConnection(); + BatchPoints batchPoints = BatchPoints.database(database) // .tag("fems", String.valueOf(deviceId)) // .build(); @@ -124,11 +138,14 @@ private void writeData(int deviceId, TreeBasedTable data) * * @param device * @param data + * @throws OpenemsException */ - private void writeDataToOldMiniMonitoring(OLD_MetadataDevice device, TreeBasedTable data) { - int deviceId = device.getIdOpt().orElse(0); + private void writeDataToOldMiniMonitoring(Edge edge, int influxId, TreeBasedTable data) + throws OpenemsException { + InfluxDB influxDB = getInfluxDbConnection(); + BatchPoints batchPoints = BatchPoints.database(database) // - .tag("fems", String.valueOf(deviceId)) // + .tag("fems", String.valueOf(influxId)) // .build(); for (Entry> entry : data.rowMap().entrySet()) { @@ -146,7 +163,7 @@ private void writeDataToOldMiniMonitoring(OLD_MetadataDevice device, TreeBasedTa // convert channel ids to old identifiers if (channel.equals("ess0/Soc")) { fields.put("Stack_SOC", value); - device.setSoc(value.intValue()); + edge.setSoc(value.intValue()); } else if (channel.equals("meter0/ActivePower")) { fields.put("PCS_Grid_Power_Total", value * -1); } else if (channel.equals("meter1/ActivePower")) { @@ -192,51 +209,51 @@ private void writeDataToOldMiniMonitoring(OLD_MetadataDevice device, TreeBasedTa * @return */ private Optional parseValue(String channel, Object value) { - // if (value == null) { - // return Optional.empty(); - // } - // // convert JsonElement - // if (value instanceof JsonElement) { - // JsonElement jValueElement = (JsonElement) value; - // if (jValueElement.isJsonPrimitive()) { - // JsonPrimitive jValue = jValueElement.getAsJsonPrimitive(); - // if (jValue.isNumber()) { - // try { - // // Avoid GSONs LazilyParsedNumber - // value = NumberFormat.getInstance().parse(jValue.toString()); - // } catch (ParseException e) { - // log.error("Unable to parse Number: " + e.getMessage()); - // value = jValue.getAsNumber(); - // } - // } else if (jValue.isBoolean()) { - // value = jValue.getAsBoolean(); - // } else if (jValue.isString()) { - // value = jValue.getAsString(); - // } - // } - // } - // if (value instanceof Number) { - // Number numberValue = (Number) value; - // if (numberValue instanceof Integer) { - // return Optional.of(numberValue.intValue()); - // } else if (numberValue instanceof Double) { - // return Optional.of(numberValue.doubleValue()); - // } else { - // return Optional.of(numberValue); - // } - // } else if (value instanceof Boolean) { - // return Optional.of((Boolean) value); - // } else if (value instanceof String) { - // return Optional.of((String) value); - // } - // log.warn("Unknown type of value [" + value + "] channel [" + channel + "]. - // This should never happen."); + if (value == null) { + return Optional.empty(); + } + // convert JsonElement + if (value instanceof JsonElement) { + JsonElement jValueElement = (JsonElement) value; + if (jValueElement.isJsonPrimitive()) { + JsonPrimitive jValue = jValueElement.getAsJsonPrimitive(); + if (jValue.isNumber()) { + try { + // Avoid GSONs LazilyParsedNumber + value = NumberFormat.getInstance().parse(jValue.toString()); + } catch (ParseException e) { + log.error("Unable to parse Number: " + e.getMessage()); + value = jValue.getAsNumber(); + } + } else if (jValue.isBoolean()) { + value = jValue.getAsBoolean(); + } else if (jValue.isString()) { + value = jValue.getAsString(); + } + } + } + if (value instanceof Number) { + Number numberValue = (Number) value; + if (numberValue instanceof Integer) { + return Optional.of(numberValue.intValue()); + } else if (numberValue instanceof Double) { + return Optional.of(numberValue.doubleValue()); + } else { + return Optional.of(numberValue); + } + } else if (value instanceof Boolean) { + return Optional.of((Boolean) value); + } else if (value instanceof String) { + return Optional.of((String) value); + } + log.warn("Unknown type of value [" + value + "] channel [" + channel + "]. This should never happen."); return Optional.empty(); } @Override public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { + InfluxDB influxDB = getInfluxDbConnection(); return InfluxdbUtils.queryHistoricData(influxDB, this.database, deviceIdOpt, fromDate, toDate, channels, resolution); } @@ -259,117 +276,106 @@ public Optional getChannelValue(int deviceId, ChannelAddress channelAddr */ @Override public void write(int edgeId, JsonObject jData) throws OpenemsException { - // TODO - log.info("Would write..."); - // TreeBasedTable data = TreeBasedTable.create(); - // for (OLD_MetadataDevice device : devices) { - // int deviceId = device.getIdOpt().orElse(0); - // - // // get existing or create new DeviceCache - // DeviceCache deviceCache = this.deviceCacheMap.get(deviceId); - // if (deviceCache == null) { - // deviceCache = new DeviceCache(); - // this.deviceCacheMap.put(deviceId, deviceCache); - // } - // - // // Sort incoming data by timestamp - // TreeMap sortedData = new TreeMap(); - // for (Entry entry : jData.entrySet()) { - // try { - // Long timestamp = Long.valueOf(entry.getKey()); - // JsonObject jChannels; - // jChannels = JsonUtils.getAsJsonObject(entry.getValue()); - // sortedData.put(timestamp, jChannels); - // } catch (OpenemsException e) { - // // TODO log.error("Data error: " + e.getMessage()); - // System.out.println("Data error: " + e.getMessage()); - // } - // } - // - // // Prepare data table. Takes entries starting with eldest timestamp - // (ascending - // // order) - // for (Entry dataEntry : sortedData.entrySet()) { - // Long timestamp = dataEntry.getKey(); - // JsonObject jChannels = dataEntry.getValue(); - // - // if (jChannels.entrySet().size() == 0) { - // // no channel values available. abort. - // continue; - // } - // - // // Check if cache is valid (it is not elder than 5 minutes compared to this - // // timestamp) - // long cacheTimestamp = deviceCache.getTimestamp(); - // if (timestamp < cacheTimestamp) { - // // incoming data is older than cache -> do not apply cache - // } else { - // // incoming data is more recent than cache - // // update cache timestamp - // deviceCache.setTimestamp(timestamp); - // - // if (timestamp < cacheTimestamp + 5 * 60 * 1000) { - // // cache is valid (not elder than 5 minutes) - // // add cache data to write data - // for (Entry cacheEntry : deviceCache.getChannelCacheEntries()) - // { - // String channel = cacheEntry.getKey(); - // Object value = cacheEntry.getValue(); - // data.put(timestamp, channel, value); - // } - // } else { - // // cache is not anymore valid (elder than 5 minutes) - // // clear cache - // if (cacheTimestamp != 0l) { - // System.out.println("Invalidate cache for device [" + deviceId + "]. This - // timestamp [" - // + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); - // // TODO log.info("Invalidate cache for device [" + deviceId + "]. This - // timestamp - // // [" + - // // timestamp - // // + "]. Cache timestamp [" + cacheTimestamp + "]"); - // } - // deviceCache.clear(); - // } - // - // // add incoming data to cache (this replaces already existing cache values) - // for (Entry channelEntry : jChannels.entrySet()) { - // String channel = channelEntry.getKey(); - // Optional valueOpt = this.parseValue(channel, - // channelEntry.getValue()); - // if (valueOpt.isPresent()) { - // Object value = valueOpt.get(); - // deviceCache.putToChannelCache(channel, value); - // } - // } - // } - // - // // add incoming data to write data - // for (Entry channelEntry : jChannels.entrySet()) { - // String channel = channelEntry.getKey(); - // Optional valueOpt = this.parseValue(channel, - // channelEntry.getValue()); - // if (valueOpt.isPresent()) { - // Object value = valueOpt.get(); - // data.put(timestamp, channel, value); - // } - // } - // } - // - // // Write data to default location - // writeData(deviceId, data); - // } - // - // // Hook to continue writing data to old Mini monitoring - // // TODO remove after full migration - // for ( - // - // OLD_MetadataDevice device : devices) { - // if (device.getProductType().equals("MiniES 3-3")) { - // writeDataToOldMiniMonitoring(device, data); - // break; - // } - // } + Optional edgeOpt = this.metadataService.getEdge(edgeId); + if (edgeOpt.isPresent()) { + Edge edge = edgeOpt.get(); + Optional influxIdOpt = InfluxdbUtils.parseNumberFromName(edge.getName()); + if (influxIdOpt.isPresent()) { + int influxId = influxIdOpt.get(); + + TreeBasedTable data = TreeBasedTable.create(); + + // get existing or create new DeviceCache + DeviceCache deviceCache = this.deviceCacheMap.get(influxId); + if (deviceCache == null) { + deviceCache = new DeviceCache(); + this.deviceCacheMap.put(influxId, deviceCache); + } + + // Sort incoming data by timestamp + TreeMap sortedData = new TreeMap(); + for (Entry entry : jData.entrySet()) { + try { + Long timestamp = Long.valueOf(entry.getKey()); + JsonObject jChannels; + jChannels = JsonUtils.getAsJsonObject(entry.getValue()); + sortedData.put(timestamp, jChannels); + } catch (OpenemsException e) { + log.error("Data error: " + e.getMessage()); + } + } + + // Prepare data table. Takes entries starting with eldest timestamp (ascending + // order) + for (Entry dataEntry : sortedData.entrySet()) { + Long timestamp = dataEntry.getKey(); + JsonObject jChannels = dataEntry.getValue(); + + if (jChannels.entrySet().size() == 0) { + // no channel values available. abort. + continue; + } + + // Check if cache is valid (it is not elder than 5 minutes compared to this + // timestamp) + long cacheTimestamp = deviceCache.getTimestamp(); + if (timestamp < cacheTimestamp) { + // incoming data is older than cache -> do not apply cache + } else { + // incoming data is more recent than cache + // update cache timestamp + deviceCache.setTimestamp(timestamp); + + if (timestamp < cacheTimestamp + 5 * 60 * 1000) { + // cache is valid (not elder than 5 minutes) + // add cache data to write data + for (Entry cacheEntry : deviceCache.getChannelCacheEntries()) { + String channel = cacheEntry.getKey(); + Object value = cacheEntry.getValue(); + data.put(timestamp, channel, value); + } + } else { + // cache is not anymore valid (elder than 5 minutes) + // clear cache + if (cacheTimestamp != 0l) { + log.info("Edge [" + edge.getName() + "]: invalidate cache for influxId [" + influxId + + "]. This timestamp [" + timestamp + "]. Cache timestamp [" + cacheTimestamp + + "]"); + } + deviceCache.clear(); + } + + // add incoming data to cache (this replaces already existing cache values) + for (Entry channelEntry : jChannels.entrySet()) { + String channel = channelEntry.getKey(); + Optional valueOpt = this.parseValue(channel, channelEntry.getValue()); + if (valueOpt.isPresent()) { + Object value = valueOpt.get(); + deviceCache.putToChannelCache(channel, value); + } + } + } + + // add incoming data to write data + for (Entry channelEntry : jChannels.entrySet()) { + String channel = channelEntry.getKey(); + Optional valueOpt = this.parseValue(channel, channelEntry.getValue()); + if (valueOpt.isPresent()) { + Object value = valueOpt.get(); + data.put(timestamp, channel, value); + } + } + } + + // Write data to default location + writeData(influxId, data); + + // Hook to continue writing data to old Mini monitoring + // TODO remove after full migration + if (edge.getProducttype().equals("MiniES 3-3")) { + writeDataToOldMiniMonitoring(edge, influxId, data); + } + } + } } } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java index 358b1082633..f55a24290aa 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java @@ -8,6 +8,8 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.influxdb.InfluxDB; import org.influxdb.dto.Query; @@ -101,186 +103,196 @@ public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Op return j; } -// private static JsonObject querykWh(InfluxDB influxdb, String database, Optional fems, -// ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh) -// throws OpenemsException { -// JsonArray gridThing = getGridThing(kWh); -// JsonArray storageThing = getStorageThing(kWh); -// JsonArray things = new JsonArray(); -// things.addAll(storageThing); -// things.addAll(gridThing); -// -// JsonObject jThing = new JsonObject(); -// ArrayList productionChannels = toChannelAddressListAvg(channels, things); -// -// for (int i = 0; i < productionChannels.size(); i++) { -// /* -// * SUM data -// */ -// StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); -// query.append(productionChannels.get(i)); -// query.append("\") AS AP FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time > "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// query.append(" AND time < "); -// query.append(String.valueOf(toDate.toEpochSecond())); -// query.append("s"); -// query.append(" GROUP BY time(1s) fill(previous))"); -// -// QueryResult queryResult = executeQuery(influxdb, database, query.toString()); -// -// Double sumProduction = 0.0; -// try { -// for (Result result : queryResult.getResults()) { -// for (Series serie : result.getSeries()) { -// for (List l : serie.getValues()) { -// sumProduction = (Double) l.get(1); -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing SUM production: " + e); -// } -// -// /* -// * FIRST production data -// */ -// query = new StringBuilder("SELECT FIRST(\""); -// query.append(productionChannels.get(i)); -// query.append("\") FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time > "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// query.append(" AND time < "); -// query.append(String.valueOf(toDate.toEpochSecond())); -// query.append("s"); -// -// queryResult = executeQuery(influxdb, database, query.toString()); -// -// int second = 0; -// try { -// for (Result result : queryResult.getResults()) { -// for (Series serie : result.getSeries()) { -// for (List l : serie.getValues()) { -// Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); -// ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); -// if (timestamp.equals(fromDate)) { -// System.out.println("Parsing FIRST: nothing null"); -// } else { -// second = timestamp.getSecond(); -// } -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing FIRST production: " + e); -// } -// -// /* -// * LAST data -// */ -// query = new StringBuilder("SELECT LAST(\""); -// query.append(productionChannels.get(i)); -// query.append("\") FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time < "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// -// queryResult = executeQuery(influxdb, query.toString(), database); -// -// try { -// if (queryResult.getResults() != null) { -// for (Result result : queryResult.getResults()) { -// if (result.getSeries() != null) { -// for (Series serie : result.getSeries()) { -// if (serie.getValues() != null) { -// for (List l : serie.getValues()) { -// if (l.get(1) != null) { -// sumProduction += (Double) l.get(1) * second; -// } -// } -// } -// } -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing LAST production: " + e); -// } -// -// Double avg = sumProduction / 3600 / 1000; -// -// JsonObject element = new JsonObject(); -// element.addProperty("value", avg); -// element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); -// jThing.add(productionChannels.get(i).toString(), element); -// } -// -// return jThing; -// } + // private static JsonObject querykWh(InfluxDB influxdb, String database, + // Optional fems, + // ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int + // resolution, JsonObject kWh) + // throws OpenemsException { + // JsonArray gridThing = getGridThing(kWh); + // JsonArray storageThing = getStorageThing(kWh); + // JsonArray things = new JsonArray(); + // things.addAll(storageThing); + // things.addAll(gridThing); + // + // JsonObject jThing = new JsonObject(); + // ArrayList productionChannels = toChannelAddressListAvg(channels, + // things); + // + // for (int i = 0; i < productionChannels.size(); i++) { + // /* + // * SUM data + // */ + // StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT + // MEAN(\""); + // query.append(productionChannels.get(i)); + // query.append("\") AS AP FROM data WHERE "); + // if (fems.isPresent()) { + // query.append("fems = '"); + // query.append(fems.get()); + // query.append("' AND "); + // } + // query.append("time > "); + // query.append(String.valueOf(fromDate.toEpochSecond())); + // query.append("s"); + // query.append(" AND time < "); + // query.append(String.valueOf(toDate.toEpochSecond())); + // query.append("s"); + // query.append(" GROUP BY time(1s) fill(previous))"); + // + // QueryResult queryResult = executeQuery(influxdb, database, query.toString()); + // + // Double sumProduction = 0.0; + // try { + // for (Result result : queryResult.getResults()) { + // for (Series serie : result.getSeries()) { + // for (List l : serie.getValues()) { + // sumProduction = (Double) l.get(1); + // } + // } + // } + // } catch (Exception e) { + // System.out.println("Error parsing SUM production: " + e); + // } + // + // /* + // * FIRST production data + // */ + // query = new StringBuilder("SELECT FIRST(\""); + // query.append(productionChannels.get(i)); + // query.append("\") FROM data WHERE "); + // if (fems.isPresent()) { + // query.append("fems = '"); + // query.append(fems.get()); + // query.append("' AND "); + // } + // query.append("time > "); + // query.append(String.valueOf(fromDate.toEpochSecond())); + // query.append("s"); + // query.append(" AND time < "); + // query.append(String.valueOf(toDate.toEpochSecond())); + // query.append("s"); + // + // queryResult = executeQuery(influxdb, database, query.toString()); + // + // int second = 0; + // try { + // for (Result result : queryResult.getResults()) { + // for (Series serie : result.getSeries()) { + // for (List l : serie.getValues()) { + // Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) + // l.get(0)).doubleValue()); + // ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, + // fromDate.getZone()); + // if (timestamp.equals(fromDate)) { + // System.out.println("Parsing FIRST: nothing null"); + // } else { + // second = timestamp.getSecond(); + // } + // } + // } + // } + // } catch (Exception e) { + // System.out.println("Error parsing FIRST production: " + e); + // } + // + // /* + // * LAST data + // */ + // query = new StringBuilder("SELECT LAST(\""); + // query.append(productionChannels.get(i)); + // query.append("\") FROM data WHERE "); + // if (fems.isPresent()) { + // query.append("fems = '"); + // query.append(fems.get()); + // query.append("' AND "); + // } + // query.append("time < "); + // query.append(String.valueOf(fromDate.toEpochSecond())); + // query.append("s"); + // + // queryResult = executeQuery(influxdb, query.toString(), database); + // + // try { + // if (queryResult.getResults() != null) { + // for (Result result : queryResult.getResults()) { + // if (result.getSeries() != null) { + // for (Series serie : result.getSeries()) { + // if (serie.getValues() != null) { + // for (List l : serie.getValues()) { + // if (l.get(1) != null) { + // sumProduction += (Double) l.get(1) * second; + // } + // } + // } + // } + // } + // } + // } + // } catch (Exception e) { + // System.out.println("Error parsing LAST production: " + e); + // } + // + // Double avg = sumProduction / 3600 / 1000; + // + // JsonObject element = new JsonObject(); + // element.addProperty("value", avg); + // element.addProperty("type", + // JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); + // jThing.add(productionChannels.get(i).toString(), element); + // } + // + // return jThing; + // } -// private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { -// JsonArray gridThing = new JsonArray(); -// for (Entry entry : kWh.entrySet()) { -// String thingId = entry.getKey(); -// if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { -// gridThing.add(thingId); -// } -// } -// return gridThing; -// } -// -// private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { -// JsonArray storageThing = new JsonArray(); -// for (Entry entry : kWh.entrySet()) { -// String thingId = entry.getKey(); -// if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { -// storageThing.add(thingId); -// } -// } -// return storageThing; -// } -// -// private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) -// throws OpenemsException { -// ArrayList channelAddresses = new ArrayList<>(); -// for (Entry entry : channels.entrySet()) { -// String thingId = entry.getKey(); -// JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); -// for (JsonElement channelElement : channelIds) { -// String channelId = JsonUtils.getAsString(channelElement); -// if (channelId.contains("ActivePower")) { -// String name = thingId + "/" + channelId; -// boolean isGridOrStorage = false; -// for (int i = 0; i < things.size(); i++) { -// if (JsonUtils.getAsString(things.get(i)).equals(name)) { -// isGridOrStorage = true; -// } -// } -// if (!isGridOrStorage) { -// channelAddresses.add(thingId + "/" + channelId); -// } -// } -// } -// } -// return channelAddresses; -// } + // private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException + // { + // JsonArray gridThing = new JsonArray(); + // for (Entry entry : kWh.entrySet()) { + // String thingId = entry.getKey(); + // if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { + // gridThing.add(thingId); + // } + // } + // return gridThing; + // } + // + // private static JsonArray getStorageThing(JsonObject kWh) throws + // OpenemsException { + // JsonArray storageThing = new JsonArray(); + // for (Entry entry : kWh.entrySet()) { + // String thingId = entry.getKey(); + // if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { + // storageThing.add(thingId); + // } + // } + // return storageThing; + // } + // + // private static ArrayList toChannelAddressListAvg(JsonObject channels, + // JsonArray things) + // throws OpenemsException { + // ArrayList channelAddresses = new ArrayList<>(); + // for (Entry entry : channels.entrySet()) { + // String thingId = entry.getKey(); + // JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + // for (JsonElement channelElement : channelIds) { + // String channelId = JsonUtils.getAsString(channelElement); + // if (channelId.contains("ActivePower")) { + // String name = thingId + "/" + channelId; + // boolean isGridOrStorage = false; + // for (int i = 0; i < things.size(); i++) { + // if (JsonUtils.getAsString(things.get(i)).equals(name)) { + // isGridOrStorage = true; + // } + // } + // if (!isGridOrStorage) { + // channelAddresses.add(thingId + "/" + channelId); + // } + // } + // } + // } + // return channelAddresses; + // } private static String toChannelAddressList(JsonObject channels) throws OpenemsException { ArrayList channelAddresses = new ArrayList<>(); @@ -309,4 +321,15 @@ private static QueryResult executeQuery(InfluxDB influxdb, String database, Stri } return queryResult; } + + private final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); + + public static Optional parseNumberFromName(String name) { + Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); + if (matcher.find()) { + String nameNumberString = matcher.group(1); + return Optional.ofNullable(Integer.parseInt(nameNumberString)); + } + return Optional.empty(); + } } diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd index 20b8c70aadf..e6ebe3d7754 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -16,9 +16,9 @@ Export-Package: \ -buildpath: \ osgi.enroute.base.api;version=2.1,\ - com.google.gson,\ com.google.guava,\ - io.openems.wrapper.websocket;version=latest + io.openems.wrapper.websocket;version=latest,\ + com.google.gson;version=2.8 -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.wrapper/influxdb.bnd b/io.openems.wrapper/influxdb.bnd deleted file mode 100644 index ec30bce173a..00000000000 --- a/io.openems.wrapper/influxdb.bnd +++ /dev/null @@ -1,19 +0,0 @@ --dsannotations: * --metatypeannotations: * -Service-Component: * - -Bundle-DocURL: https://github.com/TooTallNate/Java-WebSocket -Bundle-License: https://github.com/TooTallNate/Java-WebSocket/blob/8ef67b46ecc927d5521849dcc2d85d10f9789c20/LICENSE -Bundle-Description: This repository contains a barebones WebSocket server and client implementation written \ - in 100% Java. The underlying classes are implemented using the Java ServerSocketChannel and SocketChannel \ - classes, which allows for a non-blocking event-driven model (similar to the WebSocket API for web browsers). \ - Implemented WebSocket protocol versions are: Hixie 75, Hixie 76, Hybi 10, and Hybi 17 -Bundle-Version: 1.3.7 - -Include-Resource: @jar/Java-WebSocket-1.3.7.jar, OSGI-OPT/src=@jar/Java-WebSocket-1.3.7-sources.jar - - --exportcontents: org.java_websocket, org.java_websocket.drafts, org.java_websocket.exceptions, org.java_websocket.handshake, org.java_websocket.server, org.java_websocket.framing --sources: false - -Bundle-Version: 1.3.7 \ No newline at end of file From 9547e022181bf324cb159c400de12d236c10f4ee Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 22 Feb 2018 21:39:36 +0100 Subject: [PATCH 093/156] Cleaning. --- .../io/openems/backend/metadata/api/Edge.java | 4 ++ .../metadata/api/OLD_MetadataDevice.java | 51 ------------------ .../metadata/api/OLD_MetadataDeviceModel.java | 14 ----- .../metadata/api/OLD_MetadataDevices.java | 46 ---------------- .../openems/backend/metadata/odoo/Odoo.java | 15 ------ io.openems.wrapper/bnd.bnd | 3 +- .../jar/Java-WebSocket-1.3.7-sources.jar | Bin 124225 -> 0 bytes .../jar/Java-WebSocket-1.3.7.jar | Bin 111418 -> 0 bytes io.openems.wrapper/websocket.bnd | 16 ++++++ 9 files changed, 22 insertions(+), 127 deletions(-) delete mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java delete mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java delete mode 100644 io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java delete mode 100644 io.openems.wrapper/jar/Java-WebSocket-1.3.7-sources.jar delete mode 100644 io.openems.wrapper/jar/Java-WebSocket-1.3.7.jar create mode 100644 io.openems.wrapper/websocket.bnd 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 dbc832fea29..04356d47cd7 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 @@ -58,6 +58,10 @@ public JsonObject getConfig() { return this.jConfig; } + public String getProducttype() { + return producttype; + } + public boolean isOnline() { return this.isOnline; } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java deleted file mode 100644 index d37364c24a1..00000000000 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevice.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.openems.backend.metadata.api; - -import java.util.Optional; - -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.Device; - -public interface OLD_MetadataDevice extends Device { - - @Override - public default Optional getIdOpt() { - return Device.parseNumberFromName(this.getName()); - } - - String getName(); - - String getComment(); - - String getState(); - - String getProductType(); - - JsonObject getOpenemsConfig(); - - void setOpenemsConfig(JsonObject j); - - void setState(String active); - - void setSoc(int value); - - void setLastMessage(); - - void setLastUpdate(); - - void setIpV4(String value); - - void writeObject() throws OpenemsException; - - public default JsonObject toJsonObject() { - JsonObject j = new JsonObject(); - j.addProperty("name", this.getName()); - j.addProperty("comment", this.getComment()); - j.addProperty("id", this.getIdOpt().orElse(0)); - // j.add("openemsConfig", this.getOpenemsConfig()); - j.addProperty("productType", this.getProductType()); - j.addProperty("state", this.getState()); - return j; - } -} \ No newline at end of file diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java deleted file mode 100644 index 029b4770862..00000000000 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDeviceModel.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.openems.backend.metadata.api; - -import io.openems.common.exceptions.OpenemsException; - -public interface OLD_MetadataDeviceModel { - /** - * Gets the devices for this apikey. - * - * @param apikey - * @return device or null - * @throws OpenemsException - */ - public OLD_MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException; -} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java deleted file mode 100644 index 2a49d29fd9f..00000000000 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/OLD_MetadataDevices.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.openems.backend.metadata.api; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import com.google.gson.JsonArray; - -public class OLD_MetadataDevices implements Iterable { - private List devices = new ArrayList<>(); - - public Set getNames() { - Set names = new HashSet<>(); - for (OLD_MetadataDevice device : this.devices) { - names.add(device.getName()); - } - return names; - } - - public String getNamesString() { - return String.join(",", this.getNames()); - } - - public boolean isEmpty() { - return this.devices.isEmpty(); - } - - public void add(OLD_MetadataDevice device) { - this.devices.add(device); - } - - @Override - public Iterator iterator() { - return this.devices.iterator(); - } - - public JsonArray toJson() { - JsonArray j = new JsonArray(); - for (OLD_MetadataDevice device : this.devices) { - j.add(device.toJsonObject()); - } - return j; - } -} diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index c144128461d..9c4b60e0f0f 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -276,19 +276,4 @@ private void write(Edge edge, FieldValue fieldValue, boolean debounce) { } } } - - // public Optional getUser(int id) { - // return Optional.ofNullable(this.users.get(id)); - // } - // - // public Optional getDevice(int id) { - // return Optional.ofNullable(this.devices.get(id)); - // } - // - // // private - // protected final ConcurrentMap devices = new - // ConcurrentHashMap<>(); - // protected final ConcurrentMap users = new - // ConcurrentHashMap<>(); - } diff --git a/io.openems.wrapper/bnd.bnd b/io.openems.wrapper/bnd.bnd index bbeff87cc58..1627a4bbef2 100644 --- a/io.openems.wrapper/bnd.bnd +++ b/io.openems.wrapper/bnd.bnd @@ -1 +1,2 @@ --sub: *.bnd \ No newline at end of file +-sub: *.bnd +-buildpath: org.java-websocket:Java-WebSocket \ No newline at end of file diff --git a/io.openems.wrapper/jar/Java-WebSocket-1.3.7-sources.jar b/io.openems.wrapper/jar/Java-WebSocket-1.3.7-sources.jar deleted file mode 100644 index 02e74b0493f137666c375330b7bc517fab790054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124225 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

      Ox&+T38%VO6_ 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

      n_rW}_rzOWxo-E4Lt4m{Ghe)~MVujZ zx9s3RJy6@|zQ3i986l(Qi1eeC?O_^}0XH>C0{TqwU)Na62YK=uG!r_ch;E2`q1SEm zhBz5pc3qRh%ur!GaAk0a z$sZqnnCiefT3^-!*szH>MeWWb4I`j44a)=VZx~^WrDh90$bt@FMlR7L)&`6US$EQG zZs&$;3kmI^s!0uBGxf25zV5y6b(lPWuJ5ttHUIGdWNz_ZvuZ;iZbZW`$7uT4Qq(#S zgCQ_n24eq-ns?Gnvl}ISqJJ{+e!nKpD9#8xz}eY?;gq@I^-ev-aaZ=lGu;b*SfYM| zX4rhV4H*%wk7E5Z!0R1dcOR5mw?^f4pZDRUC`@}p78Gc+qP=F*5Bx&v!d~HbMey0q z!T-iR3{Q*gIXMk+s}h~FgnM;1zXi3W42c?&1nj%a=d?j zMn8Um{W=T#d{2$qfla_+x`g;8W)mG88?kd>+6U^b6Hy{t|J<1nBgwKNZ}dzNNOXDj z^*>trztGa`31xi;4+tp1>3^HK_n(&je>&*@w(z_kHaP5c{Aa%Jh!qjbW0h^@u((^o zdzgtceb&ZDWjwj4UR zbDQ*8?}DC$Q+JC^;x-tCrsTtW>($*auhh>+-Gfe=bbHc~d)dimbv;E3nj0f~bV^i{ z%&}Cpzk@QY2d%!D6lORLbo`dzcZj9g+f#P_ks86V9-c^}n8oPpsXkZxtLCnOY;8k#s3&jOL7pG1DfotkKuij8^aNSSDw3`-e3)V4m$n43Q8N;xOJ z!+j@dIcFbREp@3Wu7qypKf^V{q*i4j9%W6ObfPuZ0tIm+O1R$`hZpQ?=rW+iCw26! zG51U5?1V`vB08lI%BPKPH@7b&Y$Asmp*$T!X!KytUqTO)JoH(4J!&|UrKCD_{F{Cj2r zVV$a>8#J+Qo^6_PBG9pl`8WDx{jyR0Xb@5c=;w{dIst!0X#coAxXN4q-y!)ixcb_U zgRZWeZu2+~gSs@EPqk#dQTwH4+dt(z`CGbX z6c)ecCrkM8BX-5%v^O>3Uor3)Tll)&y%<}(38!-I9&CxKAgUoye0i{tmEmn>zB3~M zy_NBE`9M#*n*@~i?hi+|;7V70yCxO53`^fKW#aJhTEq!UU(Qe8Z9xpww7ojpeb{-m z2YY_4eqD}-y6$J5{Q~X^y&2zHL?gWD4DO8#ox9tejLGkfNJfFUws&=!4PiI&B4}`N zUNk9Sn7J$8+EEhIyw`rvzArsP5{N3nnjb~l>NuZw=-CMPnkEm}WsN@dbmhhBT}FuX zWU2kwh8y~bK8}JgAV?$Bj1KmNN`>DH27~6(dBQ@K3FOzA0qgF@|06ajLbRK=-40S> z5V-T06L!V-(jU)XvA1vK32R=^66GDeJ;-mhkDAb{p(ZMcer0$Ar%~uaAq0Z+!werj z_~R)|Ggq4TPRDr02Iwmi`iBi$z(Y0mS|l`Ev$^}`GYCVrm6Tgk)^u3aHR*9LO}rFZ z=CB$Ftj<(d@_J^1J6=+2Tz&lo5UPb-h01|o=a4UvlhB}qkEBg;Tg5jHi{nfA0|@1Kx5}$OZK$iO(V#xQ#vW<zjMt5qrKRzi6fX;C*#UI+DT)L1}D;mAR{=UxUBBvMt=bs0ckO45zzZ-U^-zjq^{u zq0=s^k7$X+0kibkW(7~pct+qDm-mL&*%zEFJ%BUaT*odjhhRIY z1iPlReJy$n1ZAjwU(Yf*bL5<#U;XfhUz1SF0vT7mZ~c(K$jbeIkfnzBqdK5l z9Z$-^uZwCRkV@N}2j(hCVtTTwirkPKKN?l@9K}0`jvfM{px|9K;4b2gxuye;(=}-f zX52L;4(^MTuzqQ-RiP^sIo@-c#8N+Vn%a>;Lc)fb^!rK*Iw35}+ISmw=RiJ>y?DSh z)2O6$fPSPfDY#3kgC@v76^UMvUArI&>M|T92G=`W)!BtN?xGcIVqRg{=`5sEd%>wCpDmU5v zoH$Ya!zOCW&pe?nHCBo&F5Ai52YXQiNc&qFWDI-!>STs*;eM-hvm#%0Pp{^;^YASo z?NF6i@~9+Mzq~-;g;fUwp43?4RfiXJB?DRC`QoOd%wBOj9qhdYg#tYxLCChoDv7G* z@2iv@CIB%eM@q~TSb!| zh3@4>t7uWPpL`qjFBtkQKc~_SEgNQH4`d}24O)>ZZT+5X#Tb)Z(-s4t(m|3q*|4(P zF&fca2UKPl2RaSX_TB`l3X;?FSwvf4b&xt0KPJM3rtn(1G1RgIEtJ+dF1O5!p~Jt< zC}Tz53dnk(>-0Nqwz_d*6%YPqS~8BA+J=V1x(syRW@@dXKz+o3L;4GQS@ApV&`%gY zSvaa4>!2Hqr`m|$7+0h1EgV+bUgTPy!%W1_+gGSyOW6=3YmEn?z*eid5~m#M2blg! z4BwfdbD!e!A0-LYu7Y!7z)ug9VKJ=IITVGLwsLvEe(Z_5xClr9)BpCVeADR{X8cL$ zUF8}ob%D;}G!(gDew=S7SLxre&E{4U( z@8GS8(16iOm7SOJ{cK|j!C_x;35I;Mw~yU_`4Qp%ihi56R|Lavs!b*O*cXe9O>giM zcvL#gZ&S1o?tDdn-cLrGWU9$Gid*&{tU$d|tQZnjo~kH+F7aNgw^xAhv~^uAFAy1_|HVew9v zr)F|~KNJJ%LdMVsSon*XCGH7Mll&JJe>FGFmzvy{Hgy@>gUk-%cf0lqzYKiqqu=$V z%1jbR8f&u5lI?E(ieYKESh^0XFhgiz@n&}F{R}A)R$b`jMv+-GH3&hJv_^iwXkY~| zuK?qqqbN#BYzhm&V&bwWXy->m$g_%W&#VOs-6w;zD{L=8-<@$Vssl$r&&vTY?y&05L zRjbm%2)Ec2?Wp?CO^^9Gr1JCJuv-elXgLO$-{l!g+%@4G;E~fR9D)r>=7L!R`f?*L zcljb}RSWahPv{Br?$~}%F63@}VaekbDGQfhed=#x`e)X2&1T69$y~_Ak?dJ}WR&!c z@z(D+9&J&FId`_+6LcsB+;gpsxO*=SkI`Q@h~HPko*}YX{TaPjTF}Gag8Ov|4%oCG z!!_0tn_e!PIGCVTVORt@HK^Bq4UUpuuOJ^V#WOe%=Jl_YYTRn&=AAE$g{*eLPNIIRh35^`e~*t8IGi{A1*ut!Zic;0;PzR`2k7i-WDof_VsH zOky3fRCvknc$Ol5t9qeHNch+=L6BxM1ML;m{C|$=Ppk_IYs#bq1E$=N z(9SC0U%eoV$^35KxKfhZn9gvY)g{|UQYRHV;*<~sJz?d)Q}wyHaiO@cQDve^kCFE> zAtXe`bVQ9_IBeJgS3~h>4%nNxh^zyJ9r~y7f(RLy%X67Dgyx z8i(KD_pEs4T4+%?A?^x23mkqbFxoXIjWtmfJ=2wcE1!weMb^41T+0x&pRJ;rXH>Tt*NLYe8fBk#buk}~&Z zie}|$3|r}#mY-jX4i+i`BxK4()=3CL&DZ)kd(opU+x4nl{lZ}WAyhvL}a_$xbwf- zvYS_F;4tICg(k*8bczwLvC+;Gto49Zs|=|&y+_YZyTM@3jbXF5xttqmaZ&qKl!f%H zZ_sAZ%GIygegWfzh{it$&Q zn8h7h;2ruu+hY;2Tqh#JGJtm@BJa>_Lh@P}Ot8G$-Y7=S0-arcSvC4C0FB?UfkJfu zUh6j7dRu}?<3(2@~RC=VeZd(8#rTUNs5MlJyr5 z5}r%n?%2m%Q9_{V^>e+df$&&VJqwnc$>sy6aT4}(63vU#rwStSFG{d$rW)^Ymnc&` zmy6PQ687;&6XQ$P7L8GWJ}3Fe(w5Ir(9K#g3BO04!%wh|EKt{Q0z~9c(hhqoN>V7Q zNh**>TT&*L3UYw>2Q2yOF))WsDyvyWaq0?sZ0*~M=pNNK;o*+rRxG)>!y3;8a$9Qw zu{iW%hE$N%Oyf$S$_XTkU!QVlWA_hmErItpnL2*lsv zgFo~J&R#cWnqoQ-+Q`wPCR#d8gqD0-wa6pzp+-@C*e90TgBTA6o4K($=QAReGpYIF zwkv}Lu=DaC>(Cw;X%3CELE8^QU$kxB=xfRUU>s*x!^EN@Q3#jq{&>`&wr9J9SCZ0Q zaGGfa9=C2^DXRRLfX!?|d+J46sR=ir(LUt4tm6c~Y; z0Mp{)r&>H#+spu+!v%WGFjY;vYduSq^bBR}kMBqBVA%Bjv`FzgIq!mgJDj`H7RlW+ zn43plz-GuM6ZLZjp+%QNTD;tPP4T>-&tyPFC+NpcQONjj1v>Y#SYe~#u_*_OoXn^k z<|0-k0mF=MX9Jvf$)XXeTH{YyZ5EBLRC5&h>=1J0eBhNycz9fIGl)NSATwy@0BfSZPf^UR-RroncDN7G7{_^uCX zh@I(>r#&EkLv;>!-3Ynl}ai4%Qok;t{%mPOd$j`QYK3g=Sl# zWeB+sMP4MoZ4gye%dJ|c!TcYAC0j05Zq(2PK`C>v({??<6VmNRvNzXhkSgR#RXivy zsj{3Q$lX_@RggN&Q0+u7W{sJ0 zRlN|(O@bg*Bptv=Ny6IteIAmAdqV|>stPlrMvkidX5YXwXlw|!QvG+vJ8RYnUQHU6 zVQKAHA?z#=U$zb(3NJi@KLsBB_QZ=NN-odJzcmZ_L@c&w@D+2UJp$LMeKWdnuR$7p zN+|o$-7aUueoA`R``$!Xm=vh^VAno=1c;YC^lVNgKC~qA%4*T z&+&OOQbBa!=6OI!XXY8;KRz6GdX=w+_a*A+N`5N5Tg5GTHtCuvP#Sfup*;I^9IsFM zFEX~O4~V)eEq&B1CYgNX3B-9NvCPhhp`<_f9@&r94;TSt(w-~AP!D6Nf0flcWG3Q!Im^<1W}>GIm0O0JJ}8qKuv<~ z&*N@cLq*l5%L4DXJEf+Z)3H%!Fa=Tu9Uw!mQc1-8{q5E}%3x8H3<`cR*P%u3o6Y$t zsDj?E%!LJ_;3{qIo52jMjYF4sr;Now)0;2nnOcZq)$S_%nL6+{X<#q-;DA{- zn8?&b%nRa0xP~El?&2$w&=Kde_W%P|X;9G9`e{pi5+z|tHxG|J=+wc|D#*|<%vW3n zl;-I{S5KqwOO5T53|=|zPXeCsSL;awpPF8y>NuyCXV@Q;UOf1^5-Z2jM+^!+rpvhQMf(ok9i(K+HD=oj8 zoMF3T-p;;RQA$mk!Sp6J$HUpIg%P?lR@z)#2|JNcQhnS+f^(0ZGBiSY#wBAMQ*#l$ zwRp!)c6@$_-d3Th$7@OT(xHUIcyht8R}ycaVV073f+aeLxI|Fb&E1k$)yN;!CLp+$ z1SSe^2tCp37%gom>~oBVhtlLRxTW5KlLs{~`n1b%V0~iBzBI(^_5?GQ$6#&P>V%w9 zbLlQvQi{OE;zFMA#nR+%@!DMhKN(-&O~`g2l@#Z?9Lm{kl84NzzV@w^DzsV z)0E${wCnw4p|rAVs@IZ+9sf=69imq7m@1QC%~sL!__~$wX?J%e_iVeQhrAIxB1{3I zISxFK#YNH~kBUa+kReh5d~8|RkE|G(rEIt_pJ}XGA!J2mcmsjM!zaP^3?wBhK9Nq0 z+2}cjZ@^jhTYnZPO1cP)v^5`NE+=+^Bj=)hPJyB5a;Vu-!g^vwy@U>S5?OGJgO_G5 z5ol(j$b0nV4wUL%_TK7su7M_&1pdPQttyq)5Oax}xul-xGm?ZeSDBN)m?gn)Z+R_M z-3=!f+8646YLfZA<7Uy}bmx3=5JU?5{;0W{7K%CW!*DOl;+4Lfk_U#DuBfu4`1Hn{~f>zd-~WCwm$DTXWYsa=*BF*KBd z$#@IV&5K?@4X0D-%-}gaSM@`#?KU|@^bYytByG5p^X9KLu%?C_!n$ZG=xv{@C8)`! zGE?RP1oFJ5{k6m(^+qron?PmWCUF>LrL2dxh{QqAGbOZTCQgg*JF7XrxD!ImAzGh$vjN!{S0*L$9tq=nD4@wKq)1o)OQ{8 zxQ1G_ur@Pt3aVAmPIN(B+ab15tF|ofOjuTRWhk&0!Wr!8Iv^3fsWl*`k+uYk|d_>`g!;zTNAn*L_)ErK3=V zFnn0SY+)|#tGJfEd7l;QjO?G5EJfEH68auA7Q2_pKw|np?u+DK?XEj2VtDqa#M2){ zEE@LkZl`4rYifS4ThXI)TG3m~+-p&PDEv7%_nFOnW>|4L|0$G23gO#YBl7iF#Sz|G zq@gu6J)9r+np4Jn>p8nI6)ZFAgA$z6>4JjG-pyKT)S{dog*2uf@U0JF8h5HeWPSaf zzbHC3`El@%n~cPkOyjC+n?C}wtE|hc4MmwBr}OX>Z=Uf;OpeVD^K%!%8t8^A>3;p3 z3o}(~#~HLcBu5-KG;rE$hFq`^2@`=D)?)VihwqQppiNuH%XSUcmtJr-8#R5Wo4{o@ zUo6Gq5)?0%n4UQw7BUpiHB?;RbRT|hTkg&j0rNvgx2&{tH}aaxKm^PE`rTeW72bA! z4{gg5WmNk3mc2lS^h&E1D0(k3&r0wtlq$uQ%n2sS+<}KSIilcE8~8AA#hYUpX`#y~ zzXz-6C!K(vIZp(O4N!-oNN0Y$vv8z$$Al zFm`wCiryn|026+Oe+b-S)LF-#MI(vONfnZXeu}g2t}=7nRWqw{`{WtDdO%j{@aK7UD0J9JU~q{7WAjVhRE==o)d zUQ3(@dEP>nU*Y6ZnLFn8s{p^y2VaSMdbqHM4o45L=k&XVgEwNt06k?uK5AEa(pi5! z)kI(;Z1WHwPTj_ydYxKSMZ3IbgEw6Wxz}M(%_fVz2yOvk)5VN7Mu%WQEr9aKyMuh9 z$9KSnmNL%9F6xbJE20JG@r&L!O+ltq6n`dBBgNJ>5;1t~Z~e`wWtnfBAj{bTonBZh z2NEtfzz7C;f&iZbAo6k2gB<=2xIHt5*ac5>3xkK%OltOdY^(Zx0i(7L$gsWoh9b;$ zaH)LaFIZH6#vLrl;jm(YVpnBY7wL8mNF|!b?dJOQqNUJ$qt?qg4Ti`uY=t>g*?AL1 znB0=_uwBo^$F9?6F%-~4e6q_R#=kbon9a~}pDC~nGap9NN$RT|TF~ODncZ(~{(*dRo0=9rtorzY008KlO=${a*@P-Up> zrI=M=V;1$%TO8{#y$Pw8gUbA*pG(-^khQtZE7Qc< z14~Gs_1NAt1V8oIx`fl-G(6vlA%19vPhkvy#2tW2+c#!Vn-=XkHX;6Y^!B%t41#^4 zrCPkY2O1o#V2Q-EMp>bC7iTU9{)7%px4QLjlG;@sou#<0w%dAuD{%<93cd`tHP%Y2 z1BlaVn&M!=3P=&NdvNiv>Meb!q<-F0dvN*q8X73E@Ij-;HlTvz8*~exE7U$=DmH=U zDm*9qK%AeaXFUJxfdsfew{e6vOx7F^e&?C!etElP4cjRdM;$dXp2& z4IrUuGL~`lEJ{VA5+br`PA}NP7TO$n@X{S<{+o393#MJL(y+YTzAvRtOx8rCRqEMA z%pL=GXhdQ|8)Rrz1lg|k+X5;48Ij$IjQRbTe5pb2_*ZpU9dd9_6&pSEe@K^_Utn9|!&6_AV1P+ZV#sqk!SK?~OCW54riQ=Ll8i7(Q7bqWv^M-X`@&PrxB< zMbrzz+3dgZWwg^e>dF(>!xhF15Xh4EvF8O9z?8Kg_Ps@~ndO2+Nq=V%5IQ7fRm9=l ziK2t-pYF6^?PGfx&wC%cLag~BCET?ZaWlLYixB9@=s=pZFZ9IpmN2pD3Sztsx_~_J zWMPoNjgGV4?HYgxr?_^p3hdzchq|Q4_P^O4_T-SF8z9S#;9u)_N-TC|da?gQT{1hq zZ6J-^kGr3ut-}=(m>PNM`i=H8T%#}dcPQ@${2Q=Cgpl_R5dWbrT|fPCT|{jO1SFPG z4tOD$|0YQ63WR>0^fvxeryuwhK=JJiJ~oyY7BD_5+pzBMM65Ukd_43yXwEm75*dsL;rCOg(4QmpCt@%Hu~NQRqOiA<`DsI8!YraW9*t zph3zm)~24-#7k#t8YYyy2kTcgVQl9HGX+G@$giuU5(cm+Hu1qogR9_e1NJ(jOrdF( zm?GSriugi6YNsa|aguys-w8)MB|m>N<%{gAI4Pqs4=@K0rrC>rf*C<^uNZ?w5NEZhj}!qTq|;g7TG9OF3aFYzgBc9E9C93UY?;ylr<@B958KyX1F^Z5?@Fw4m~**SjznVhuv>*-N$! zz1)QVSaYA+)ngwi-F>~|tsF#}a(Z_E;QzMDkV);RCMr-$D{CxKMxd)Sb-%-BY)hL}xAME+Rd*!Eh&cKW4 z0Dz_}06_WwxeAnJ{*T+>zY0S~ytd98!*h8VSU+J=ybB6caKm<4{Kh=^3&?2osgiJ# zBl7&Fg1ZvLz(D8_;bu5rFV_=WdjQ1b%eE_)2a|OX;Ol3%muDZ0bH9&Mn!9RF>DCWg zt!n2vIJu$5n_S-)(C;U8rss#|yzufLYg{mE9jS?-=1KMhCB@8eiHPROLn8*YjJ!kU z%Lr5Q5{WcojgK{{XOUcJfwidWNtH}Sf0u+0MPxFXy(j?u$k7|NLAguD&8JL%tir2Q ziO?a40#d0^eS+lmNkTwABG65$Xln0XB6}#+ zZQ}H|D)>Km^mT$Yb^%Yp3SPb+vRq|mVj^dN+b>~Tia242SkgJ)KcU}xJry`#zr2}X z#He8IUr~tM;a^cmA%o}_HH1*aZfJ|8SlCMW3$6)$m=Sw>x*w)Ym=P<=%EFj@(ZgOl zC)VWdOH%GDW2U~`_6%56I{ffM-q7JM*kOUF)}p)i?uxx1J^8T4jC7fy)2*i^3y*D- zqPw-FkB7Ne@jGz#I0|P+HZP7$ki)lNxusjN0W1)px|`62MrjXr50(z>DZlH8sJY+K z{tSS7K^=tBhq<+n%03mHo;*EyGhxBQZ1*^dgSl5D=o7ddxDjWB=`eGnSCmg;v$UW} z8g7?c{O}CqQuCyXys{z!h7;IbCns0_U=I6G|I^*6rFaCeUo$n}Mecl#{2m;yjuP4w z04u2pO!?5;Jtv8oy+feS8yo)2=!A;KF%SL_eQ&`x7rPff%&Aa5ru0}de^%kPbh%wZ z6Ly;{MAB7Wd}hkd@N=I&I;HZ}tf`vN zF>0y9s1J0R66*cWfQe4&S=8BZ94-(Vr9Z|S{NPSeS`H>tGKm$c>K=;cG30a`U7*{{ z5ag}Q+JW-~x|VOQyJYEiqzUzfs^*^)^`3T2D5PycJD$6 z>b)_2QoOBcyrcv3Y2-d6dtt_XpNM&H`#frlQ+)u8>M3w^-}?rvhidm5HW9~A^iXc_j2ha@V60hL^Z-u^&3^z8+?yiDED#PvL?#E&7U+CR z0&K(9i1RhI_V3s=x?~ktgC^wy$yNZoq0<9|R1AKv!HSQ@^k5*}1~CS_V9mp1$C7y| z*HZ;H6Ol`BWDx@mj$H-#J&vW#adW~WQufYN6~S@VlHG}RO3yuz`uB&r2kZcDpiZpl z85V#w0)hIS>!%;C9()OS+wYZ#5$!w{Mn2RUegqy1^Ck@7P^Fgi(U5M7Yj^h>DR*A{ zRO_Iz_){hb?s8hEIqr-n)VvkRdT5Z{)Dx#RHE{4m&+l0!4ss1KBtwn>LzIBPVC+BR z?n=LO>ZBzrP_xuKWI82GNN5MFHH+WYM^PidBT&B}#kYxlf+G;=Lzx{JcfH_K2_mX}}*%VdBHe3Z+Clj@l**ODL(HGgEW!w7{v{^t+bUW9eLoS#@ zRsssWG|gc>M66YP%T{U^O(FR2!CBrL+s{1}m3q4yE6>b3wW8S}-8tNqdWoF%W$>3T z?62otFHbXv2#40+!4w%2F|NFx!HdtPtb-&HIt~EnVCj^YR_6`x<25vM4<{*0x=4Cn z?^6ie-CX_ZLCR#UPw+6sjDh3JV@+9xICs(*E;BRx9VJZRsuM5-gLerZ8Ra?PYa*J3 zuPCDU1~mf_T8k9CzETg7&4|)yagIm|iL3Wi?Ctai{$P!oa=pXs;*Ofk9WFM&evf8nAD?Zd~=pelQOiXCNhT{gQ7knTn)z)Iq+ z(yCeRAim4m79&q;@~8on`Uz`<=6cGQZufQwK`K0DgJR%R`4ZLEr383p9!M>uCRCRs z$=F_+Tm)e=f+{*6(C5eWX-pi)j3e(EueLl}QBf)$+bdN8pOg#_{mc!@C^vaxqK5)h zIRH#1G11DOXvObfO|`kca5J>QF`6@<&k_qwI-jnQs@*Uk_+3qaF^`*Iuo+?Kwri}A zTU?UD;&c%);Q|4VGZ`$!(GYNHn_Rf^CzzW%Ipl+}<4*`owqIt5)Wuz1F3@w0u3hMR zoF>=$mkcG*?oJ^ILfE?5rIp;FK8l43u zs2SEy!cO@LGP@A*PEfiq3T8vZ2#dtut73-V6{%ZYBu)bq#YVv(VvQhww-P7k$d>Rq zQN;x%K9!K$lrs(~Fc0Vs@h~2lr4-W}q!1_78@4>_bm{)zu2^P%jU=mCNY1CwNSb%l z8^?9{_ehQ#>DyJ@=^op79=72?X>XAqN)Q_59tzX z)R1CB^==C$2ZJq+LP4(uXv*{gDF;kWW#ZIWlXKo5agTQD6P z0r-0=3NXNbCFGLtD%*Z3`>nRa*u;B_h6_3eq)uQ zZ3F9=)2fX23EumV*%Hk^*)CC}5NFtQs;$Jl$~26wWvy#M57dTP))kAmgiFcz zTSg|)5j?hW^1h-b^hMlYHSPWIy>G<%b@cW6o3pDe^Y?3QHve1smqQ1Cq&YUz@0Z;p zS8kZu;;eBlbM>UP+s!}<5)U(&V0cRl2VjZT2J&|K+)RoBop*;Qbe(~X{Vl1LrC8+f zKq6fbM_|6eC_j3+K>|@_Tq!IYjpqvPZcta&hM)iy+Av8@&1ok>-vw9L`I|O!*>=9i zAquSD^cEfH0(4HS8sgFXHqw!Bn_L^NwoD%SS@owcR3{M&E->T@ikd@ZRbW%vE2w62 zwx*%R?z(oK?|fyTpyrS&S0PieTv9WK;M$dKbuzaLv|5TelgSP)UsQvM3G_mHV&Mu@ zx+N$!!OAK11K`z7+BVJUP+iPa9EDCjmgSnM$T1a0)^=&N8&%F_L1~X89Uu!B+k&&$ zeUWDPEC4&oKg~OEWAM3i;C~I*rCeRBc`V9Pm8jPh^isfu64n}0S zCLPV4wI?-+ekRL3_j5ER;WhHrKvW{Euv3qB3@vO(&8I^v>7F*pJ&HcKnIJl7P3=SX zVQHgOwN%VP1u9Qrt-dcT8 zL4cJ&=g_4oD*zFyb?e0k_aw^)>ZQmrQh0RMsK%CMkifGarUkMh(dZe%G}48zbf!!9 z1g>i8mnQg ztbg`vy23q_N?VS-G48x~zm{~pA@zDpa=J35U$(zAN^}k&rPoA?YDG2mS*Ixi!XVq- zqhjm_R@SbI${^H(?@z#nCk9bihDpM1k zXOLB)-2A@P&KLMyN5|}2l$j;XOqraI1fVT+JgcvgZD?F<6xS%9AaN+-X-L5;VN_j> z8>g!~JDvPgIH7}FQYFzz z@3CJ_dbCMB&~Q9Lr>bY@P7V^$uM%opvIyj_!H$`1rY#0^`JSt-F0DiH#TGp1#Id;lU{m8%QRwXzdk8rQ6q>A6t}5bV)7lkI z|2T^Q{Syso*ZgH6On0dhH!KS_5}`*arte@Yf|x$)HS1QTG3#lpa0{+8d#V%5h#nO{ z36LBQH#QM9%y->C>{jlYPSd0eH9TaBz30~O&6qMQ+wo=W24S>IM_$&*VkT-@^g@k$Y?A)Q4Se+fA!9c zeGZN@j>Up7bLt0Ap#&~#eZO9KH{&X7yS)1ojFE|8wyY!aKuH%`lgljj;krZYpI(2Rv>{ak0yko-0)r<_C}YLX*`Mm-~5<0jw@$b zv*)ZE_sx@~7VaaKSCVG;>_ccCXY!+EG;@H@xlF(h2Bs_F)oa716_p8|$r{#i{p;A$ z1R>lJ&DBb6WAg9&jOo5tvKb2Y&_}+^Y0K#AG|Q~qBsP>|(_hBJc&+bk)T5A5-k0Zl z!)=aR)`FhJ0)XDQw8^98Mo) z8*0b`E+%zLO#qRmarDP@<@i?b+IaCtX}vwyD?rF5r5hiPsR_wNORl_`&a(u_6C97i zW!9nhcv@43+B~Blg-s9!U6IEkd%c8p?sXfGOj=Y$N9Ku|mX#PKpzZ^^rOIrdj`czBzdJ z#_TK|8~rw3dj2w5G_R>m_UaBKoiy8-xH7Vk&J<+}oco<}F`KV&sz3Ly-(>@OEV<_8 z4b1S6?|0KXG%A8$sv?bI^EE5-=Zx#@XMEJnBgAAthWY`1`faXEUaxW7X$WfqdL3A zNmMk=(Ozr5kq9@V+pFe0AJTc08_ZAaD^(djaA6g`^00SSA7@1WpaA9aF!71KFHm2Xf6kQz z=$87tpU)D`tEay*LHcIKcrW#}+!igTvT$UJe@QlLN^O*K9*MeMYnNL+gqvnO6|qhM=*-#uL@x-j#;?bsB?&L?&C@r>Y;N5 zvCT%8n)5wtmvNyUZ3@ruMN1nd_zm*zU@+aHN+MNnf8&eK&@}l>orZ%@ccl4%$zs#t zz7P+(0aJ*4Di9%bUnn4PvDOpS^la}r8ca_9E6d8?uqnvB3FU`6{wsM}V?@fnl%oHa zlZOb3e58i~jeW5J1O<+^dFT|Obn&oKD8U~$HQ9HYkzzFPE!Z*qZ`&vmj`^E7$G#HtZM|roY=OOA82%Sf`=3wJwnC|D zAf$iWdH)1i|DPr4e~y*^BN=D@uV{QpTi>dx%y+)jZ+V*9={eo;-wdF_) zMf+aD$;WeCfz+a)R02*&>#q6xy7ezWG9HwSt37R@sa}*GJ!Fj9zlh8HR7HP&-7Y%GCS8^DkeytXdQ6%CnA zVY&~4klsLe_dpiM!hix6ry!!kOrh=2!m%{){A}6b?~R1QG~hl&(}KQ6JjTMtdBiLo z61V9kcqZ=YKW~AJ9#Nv;jD#>BBQo87$TtKubX{+X9FFMcq;*c6C;p4xixzRzFW3B5 zVP80Jgb_8(HNIHpo|1YEQUkoY2RRkeu^Y{bd!d5_YB|azqRGmmoqJX{y^Kj_+R`B2 zpWgC%r#){E<&c0}F}cdHWa!Y5&>`hMZ|Qj2e>#f-Av^WYB(WT`MgTH&tEDQIzb_ev z9%_9%y0j%KX@G3+$q=Xm~VQ+Ebae)^+$W1P+J>Gz_z%K-FYfMdPONV)CS_Rq;WTp)4`*h8&<8~t2pbjc<70zB=XrhOx9ZOd5F8#z zB~$_x7h2dx6cArn#>ef>nGGlYUWMF9(Hdy?v%yi4CfBzcC*GrmA9Hr#Y~;Y32`oxM zgW+mAXW#CeV10kgpBs$$-Ipw??}}y#;%vD0v|iUp+)1RSI|v5-D&C*%zd?kF+wDEx z2-{_BdBT)!Cu>K;bjcSMgAAZN{v9p1(w&SWL?cm+SwN6c z0mj^bXUb>;9cFokfSyiA#uKO|niC|KRPhbmA#7GUhUWSPE&qNcL%#DN+g#8m8xyyr zj5}K)3k$}RKtH59w#RLu#7(V6&n-6j^|0r4o2Hs+!bm1V%rx{-)nGlGR(a`I<14_f z*A_pBOb{PF0KfLF-He{3YRrHYc7x8$TI6m;vGaBD<(=wzub1kQkDKac-Exm#G&pd0 zMlnHFQ%U!is40A=bJAP;Znr*>?2nwI3I_qQIfNxNI~vJmDlfbT zHs8L-RV_Ndt8@ySJN+tJDzRs26;|)&Yi5RGKc2o{M~)yr#Ap(~v2PGqAS=sxlZA9y zfQ4>pEjeVqpq*IkMyx3 zMl^_rKrQP}zZOkC7aRyJY>D}Xjfd5P7b$!7DgO5kglh4a7BdNpv53h#$*;2Pi`Voa zF2Q4gAJ$T4{CV7=m#;_&Ukh}Fy`I^%IcdAsQuh$MLq z62Y;Pw1W*jvJmf)#!x;U1q%FU%GAankUC5uvU%zwgnT;o4*bzfVn!KjASp=0LC3JK z7!~ttitCR`qpZoPVT4YXiP7dnxr;vjk4BQ*j0cr<4!Ud1_J4}>rCL{v%1Q}u92r|V zp!If)-6}~;wTxyj-A4E{_NfBQ-9;y=`$ezNe)&%bDoq9BG_P@F}hd6Ask;V z25?|Hh>X+r_ALDK5QPXB&r~%{h`(-oYo~XhM%o)px{F4HI#@hB)2*^Te58%BzTe_& zB)d(+nmtQ8-K?W-jKUI!`3{}<61omuDO|Fxbh*(Qs8WW86G`Y@W&}T*Oi2h6ngnPY zObw<11Q_fYP!;pBg$uq7&KrLQ;gQTOOiiGs<$X=xJV@tXJznMnAY#=sNLZ*km-LEl znT&Y}5;V?0nb6f{kIK!V-&=B4UDSoNp{ejiC8#c zK0?Ysf#)IX*I<&dn8H9XNDXY}t7u7B*Pitej49gcqcoA1HSr*@YMAetW*Vp=9xT#-WwQ#c>g<`*oC#ko~!2OgH;tE*V+(ruV(oI=Pt1 zsGla3AvLlomo`&mWORceY>!?{a3|)TWVxj z;<5;Uu1v;|hElE~yvS7!sTE0ueFpX1o%+3cYAi(MC1kpxl)wP~nn}7#+x3#@E z%)pPpRTu@CJWC7PeCIlmCGm4fF#2)*GZpn}5~dE+?Foqa6q8;!nwD*e%~gs6NgN3% zjY>TQKOv(CDe&$R@+@s2CWCd#0aE~eNuXRzmgBOT2!UclT;gGFAF8P%frCI`N8)nm zY`7W@QQ&@Ffm&knhN4#5);UW{q={ZtTyi1IJQq9b(mRL^+Mrc^KJhOk)wH1?!<8V_vp zGo^Dij4KW|i^W*MCbb#0@D|7jG)xtUV+v5kB^KD;zX6KE5uyg$rIIO2sm;XBV!9N8 zHe-nZ&WU_fGiHdP@mzL02f=*Fdkwa5)upWBj?a*cIUZ=E5R&p1nEG4Vr@&GV7mwF} zVr-{WX5|7wX-;ZFXJ9`_f|9Ci?cXyFRt8Qh#%K~YsI%ertU}eY?Ny3 zC{_!fnXN(WVyYn^ih>2jK?}-HCH{aICl#=n{IXrzh1`fvS0i(IErkElzSn#gNa=6V zL_;hrTU(OuMYi|blZz49rRO5OrO6unYq~y#*_b-H1gp!he{m+zoHb*HQw)VQDeJ$^ zRSJmd?Ss|O(uG!Be1x;EbS#!CYD_8tL|_1Lhgx!J9^RBpGHrRFjERFe>@r8DOR>A# z=e>q&y#^3L7H7W9f+IXjPis48+W1AoD*rwsF8oPThBR`G2NNce%M`BdwArKYnu+Zz z>d_MYWh$DaSj%qPl~sXFZPGEKyH2~k5tnP_#Kxw5oEqE5=F>E*^=WE+)ojwNwDLlz z4jouw@aqX|wIY~7kAGUTLP|&gxykvBiyg2kx7Zey*wz9lLNSZAb8+L4&Tp4P zzNSiSV0BHTCdWH(x8hi2NO6E18$@C9712$sW)ZCb=j*Zb`-6N;TN;D*RiM^ZDXKOD zZH966l3OSzPDkspDw_FClNAl%T|FnH3;tpm2N;I>;1Ak|Qj%B&wh&--i4UhUD=eJ> z1u2zU!3-!@rfw5JVhr=A@Ceu?;KMGrn?ZA%n1NPA>k~fe+RX7*lRb2zug9Vf zsW9-?nO6tFQcd!<*vCclO{mCl5;()w88~OWe$3v|nCS%Vwio}f7?>s1!leE?F<@!n zQkIU8gRaHL(ZME}z1 zGL|@NCmfD<{eg6GRJ$D_h}3j-+Yp|Oc8?=#V^PT(tfU7L3q^iqfuhQJiJV8;$!J9+ z%%$Y_5IkBm*-=NvWerdRzs}vBYuAa{7E*8Z$SVaGbX>h^i45;2rTR$yM^DnL4RWdX zIZj3O!`~=lLQ~FE16-<-9=m6AxzGDCx*>c)_AP?JH^i?5c!#MRCW#{!@!-y7R3odW zEpMseFL9I>=*_VpBBIF0eFO%QOR?HX{r_kpNo62iJjV6$6pT! zr`d;KKn6;kpJHh$9X&vc+c^H{%#uAB@Y9DoHG|3o7Xyg|n5^;tahxt6obs@d(90^= zZnxd?u6Q0$yTazSZX!HObf(Y3>sEVd*b1Ztw-XScmA|MH?|Aql7W8*OOUsYKUx**o z-E(}xM1k78yV=;FSqn#e*|d8|=iR@vjk)5-1~|bu@s1%?xSlrM1_>yR?eLfPrkZ(! z+?v5$GZd>B!WI!MYOuEP1S5IaGKLndjdYgnEIZc^v+u4YHSji5!t>qLoiq}E+85;Q zBV7pO2CivR)jyrN?xiVdPJKoEud?E!oijyRMF?#e$JTrl+IMB3uNQ=2SqiW>w)`?L z8mVO-x+q{)uuhskh%M_W@G;emQ|46mztkbA@bXpSbEO8|1^Fm_%yU0 zXu+6>zyD_cvyf>aD2$TQ)F2AyL6XXJEE2uMLIAL^&B+a}03QBZC%EnCBHlPVyi41_5dKmd~ecf-)t#nOi1m!Y#MJL`Yj$b3H3 zamG?^XOf)n1s3Zlj14(}`Fh#GkzpIEL4z=EIZJtHFG%RN+No~2Z*??(-^TeNA`A&JcgY<@P3SDXR~ELM2#?^d`~mZI>4o%>wJ&qxbPX9)IbjlgUchpy;3X zitzKF(3MP`QEBPT1o42f(YCSe7w8$Ugg;An%7zPA~lHfM9T^VYp@Th6X5{1ad>F8NIjVF9zdb)Ztpn-dy#W+{%*1hTQo%m%TJ(HyBZhs{ zzUaICIfL@&E?N0c^YB_VtUE0CWzP@dWGl%Scd2je?v3gbt9Mj>#`VK}t5J^dS^ls2 zzwV)jqZcsVo{4ZHN+4)V91kosM!znD2iT9VcW9gwI#kHO^f!_qpD?5wjMnRu!1rF9 zUwY_Q+#XnX!ein0%&qe49`$(u<16OTlO}##?jFwtLWYD}sdtW~02UyCC;*<=m%N?@ zUK@-2mJ52kIS~7a*xM&W9As;HqovM^_gk+oo z%u|=I+Yai8Qep%eBThr8Dujz5NHbaI4VAvJtI(L)TL1!%>{u_Qu&D0^T8N-kg<(l+ zjOgt%O9PvpAE=R~W3oENDA%Z?^NA{7*2W7N?*#<#?-|tmW`>V11?Zs%PzK$I-H~F8 z%@G=x3Ii|R%rJx9T%FBnp~GwD=MoYXM1mwh7IH6$6sw(Z3eh1ye@Kphk&{Vvb#zo9 z4EUM4?xsDttj^r#Et`EvelZiosXfc-|I;5!6h*IUigd`cUchMxkSbQ|8g>Xl#DcRn zI56fjL18DS%L<;D5x?_-#h{-QSqqx6?m{JVLoD9|oyNjg(6-Y@6@N!K!ZQrWGo1!V zI)NEu7j$&Bp$e)OVJ<^SfTu`_A$nWhGZk(ZY_Z)wF=ofy&p#601rn1~C+=y_`lH5O z>eYOWong~`gZ+cDKp~%!91+6E$H0d#!dqsrs*dzC_AnLD->OdK! zjqvoPol*q4KgC~SUPZrLJ?{73EX{xH2?SXKaTy#wUE-K>OWObvB0-D@(yUM|z%*wT zful5mAX#S6W^j`}q~vpG)s&g$h6HUS&Vzg3kjr&;KFy!6@0PC%Y(1J`mdEz$6T~BW zd@TZf~XW1c?&m#B{N4M~#>oNUCHEV_F`uGll||UjLAql!l`QUxA8++Z03#N*Y1Kh`P_G z4|0eRjh^^2XU0&>XzeG@<;yc3v~h(4!}I9diGHX)66`1$=$__ zvWEnyGDQw+=Rt=fC#JgLCLz0tv;>Q)**`$5N2+2vUz;W#d{c@EQp^XW;JCs(NQ|2V zo8ZM8FXw6bVzusHprB$|#-xi#jm4B2R3~z<1v3I0W_3k>GDC`Fs*Qd#jR|s4{2}c9 zM5Z)RXuXxH1Bi8xnkkd)_G{+mx6d*pl21w-lERD3$|+(e@#7$gIIvj!lXWdZ*vD+j zi6W7(YKTG6dX1x)HL+?FY)9Dji8Rh)Q+A1D8)88~t#(;|m_uyUBs1h6m|H**SrFDx zijM!#thOMh7$zPn6ElWLrmrgP>ieXClYN`_>rmN)pz0a7v_VIa`iMXN8#%T!NLAo? zVge4hfls8&;*B4NPa#);WPVX4Udj)~Djb|{i2#gUzeb42$S`wch=?VGZu$WRAm&ClzyxMcBH!4fJFLWcpq@V)Ij(ON3OB@1I!Q1=bLA${wNywU!lDISpxxtxJ!W&T zr8exQ7)nYuk6h<7g1=XQj5yyUNW>XVu*;9mkq0pVD(AfWs)prTK8&FeGKTaWlxGRN z1KX5|rT)>V1>|yM!0d+>$&M1Bz1?DAA&-nvSSmPQil}m!+9+eh1Hmd~Mut?^ecvEd z$sztx=qQ9l08*JqO=6WO8%oDjF>R1U$n$+P;DK2br~-b;0U2w=`QjB~(DSPk&Ia4J zB&IVDUH8JfZK68bYEn%o8)DF!DFpirSDC>P%Ko`TQQpV_$-Dz^=(@J3Fcan6zB^nB z*2XgDxlYNdJn=8Y^o%knI+W)I*ox-xLB<432Nm>;v!p!%RHQ@Nx^NBAJ%CwuA`nn_ zehGgKrBtjXFmf|Tq%*P*;SsM99;P}9OvMoqc1AFz)*qJGTda==HFP}oc*Mh_rxJKl z8ku&t`S`{`5bd$t?`lndOQIfFkZIl=-ZX;(Vv1Wtg7_Hx63aj)5G5^wwff+n0#N4L zeya5Vs?a!)2)0buVs@oExqaXx;@m-hO1z7G*mkzMQWcz^Vs$U1@OL05iYH$*y++JV zK&HsA$Ax&MKB7HAr>+^kJY5jFn2Y>;;3wE3*ruFluZ**?d>1z?V$pyijDjzgKqw)7 z4iFv6#MpiCb-g4>1KE9@_v=`(IZ@7ac?QaDJGICNEZIv+=4UP|Fsy8%tFSu)T*5Q8 zMEIs~5wro?@i(3`lN2N_#zI)aSfqv~yIXrqf@*-+Ge6m#e+$~;jXDvNff`48&ci%l z0UI2KA}N?w@QkZSmop~uES!u}7EM8V{P7pOcs6!?FZrpyfearDct4p$Kvc1~S4ntW zJ|8ITOpxqNw1;`u=@z@BE)GmCTySfdIKlL@z6+<8i`OdG6|R*qOJ&x}gc)Re`Woh$ ztXAs5VqpqWEWWVhcy<_C z61R}U>bx!wsam?)pm2wj;1)JbwPdeao z2+Dw(iLTTER-it>gCaz~&Po=bM1R(6c3Vek51jcbIYS_#CNh~4V{-T6mN*hbnw)yA z&_+FTXDzsYJ@417Yga;awS-$W3xzfjp2)c}YlPwhrwm*ws3TfSx72|ScfA)($YGec zj!k?oC-_%Q_Lxj3pI`m+AheX+p;|zbU~?rq>;V$gXtYXhA?`gMXGh6U_MZgnbx&W1 zRleJZYB*^(YMcPk0(8s*!2-^G58vKH40E6!(`?z5DOi@)oLcMCHff|AR{Yg({3pT6 z1!-g*u|m%1FlqhT8XJdf0p{1W(bQ$wp=OlXWK`Dv5(904>L_QnFw9jP5w7_}x*sUd z%O4@~7~X9tY{LJ+*Ew}*0&LkfZCBd1RcYIHerembU1_`0wr$(CZFh~+4}BkQ_Ya8I zh}dJVHK(=n?-dnBO(F6DsGWGCV~vk7YPg{H26_NWW<%AuHhFl7&_$T>GtjPo+1u`XLGUmg-)EA{!dmXmh8HVJGQh6LO(J6QyIYx_& zomFQ-B<1~IJ}>T8cz&$13heH#Kn*)}w70{nkc4)sVXzird4Who&ldM;zY5!zEa`dz7T_bP2#$ zVDT$9I0>lhEv|}_S#>}qP@T~R4fcI=;1c4eLy5`=#L#TK8b1NClMQ+p$wuYy6z;Op zEhup=gnr}2xFih)#$IWXBdVjf1S2yK?mFo6@K}vU2<;aXYH+xPHr6i`oY2ioEGO2M z0dvFc4Gv3a63uxVX_Wd*66Dg$-og?5`;3CzEeJoV@w$!H66*nzCV@O|3Ay?pikkZq zi6T0aLQ+-x5|e1}YKgc@wDlpep1slXt4lo~R;KVAiJHMds5}390|F4h1HO>~p4T~7 zy<350DR3H`3XM<9=YFI?hU}tEq8tm$M58}i>V6gYPMBNqN*1f2Sb z>T>t^JHADQp!|@x#DZTT@2g+JjyMNsw@(;w3&Mp6Y;O|KWaqiT3FvM#M zAa4&~U+o~jxlqmeJ0I<{$qe>(Twc#O=CmY77BBX_*3E;Hzo?`Vx$=YINwshnrybY# zds~0*#=;VRr%5}JyABOE7!sIu`s|ql$*G+VwJl+Z7Gpduaq@9+UJCHXn0+IcZ8ttw zoZ7VIkeiM#uU; z?gL3MIKmU}PD8h78luR>&hU47drfLI(vag12BSfk7?h@LV&5j3ygM<&xDNd~y~kPj z#Si>&i=VidGD%_ecaSbbyqVdg`pGwKeMQ$9 z+GhI!q%NWWYMHs-TsHlK>ks6QLkds6EWDQWH2f|W%p}LZl{=aUFh)|nYc&uGJc{(+ zjB}m^qyyCD=D%5{@M(eSDEz;DMjN2!ix!Pi7ZoM(xvIg0^rJL{*mnoH2yAA3Tb=XO zQRH;A6!8wQX>Qa;sAd7H?6b$!?qg9eHLW9SziGPNSE&P=X|$$5V9{CosmudzhPpW* zbs6O;Uf*6_UODk+hOG_zxODy5Yk0S{nu;S{Na)1m4&)8+lO24!e%uWA5yi6)Qcr4t zUPiRh_CfK4x4lbj+!Qx-(GCX^9=LjF)_pEMUpBr98xYO5Qb%SHui&fsR(%*txZ$i? zsYDB&`4e$my*53>g;{;RXa1Hi{6^&&`O8UydGbt%SNFRl8{?msM=Sn);BrT|uHgAh z-Pvx3R_J-F;@ab6U@h}22q6%J(kgRi@V)(Xd{U^MhN;gU`*{psC45`|8SPLExW20$ z;n411x)b?dndLglh;ZG9@I?6Zos%aUn8*(L@#;AsZzo*+cIV`Ft4(-y=XDvobQwL+ zvO-#(|M8m|_zY6aA@-ZoHu#y?>c^fVQ>HouZ8E>Jqe3E2P`n_Nq*=5#Ss5bdC9L7$ z>tNt(VF2+%+zxQXf0H%P`Bh08r~uaVL#PlpGj$N)!NVGd_wd@!)9sCvl-JG7?>2Ry zH+8Txc@Xd6ZG#`js~|r)V>fx=mpP*R;BJcl@5~0ImM~WBPUg-|`bbaa2*Kn5KaY9H z@kx%yW#&kLNA%>uEDyjv50H^-^zbVG@H)p6J9)6mBgdog>)~OM=X-*u_?U?$uFWIs zt@t+a?Q5LJmpyLf_>KZm3+MPG)WbSsB*nuzb3~SaUih^@3ahli0db>V zq!0@0D(2Uq1L}Tvdb6SJ@Xw)H5_s?WLf*Mw!7H$)7Zmz5rYADy8k#&UM<(!R&Ent( zFvpDo

      t>R4B7lgkMIKBW1Ox+)EM(h}e^NX*)B@}919}N+zNY2ARHFGo zXkXT`8d;Xf$m*c=j@kiW*gf<|)73Z`vxzZsvDwt8!hOrDLUsIAgXB;xz#GI)Q;m0poh{kQ@Yyvav?@cEBrL9LbczulB zMeTdy;@J~C@j-^W&9HyHJ+=0QQhl9j@7xof>Ghl9+tVs>>`oLz)@A4vCg;@WEngaL zC``5wR@bv_keTlkJ|7EF1-La|`jac?^>(e3hEKZ%jBy05+fLk_lbII2Y8PUs<(Ic~ zCCvBDmuJt*#Rokd@FsHoiPh<+DN#zxI!t59*O~6wru+WOMc7Ua24F9e7F%ZwvL_pc z@_na8jST$equD;P`^9DtmG@?&pXz<9B}DnL-443s&}j!X?W5D? zn}1}3FTZLX;d7&HFQqDZo(mM+GPlz`>X{_yY^Ksk}_p53->HMeSHwUcTBmOnn9(9JV z0l(%QUy7D5Q$FS;i_+=0cC;X0muS1-e{jLosLh*UrfrSxubWAx=XTGfKi`FTMpwWy zFBWL1HAbIW+-jHa^v+*Wv#~vSMsU8zvd3Rh+{3$%^aoQZ$D=oxK?~gc2#rf{%{&)w zZX>IBdLDkBRBXEoV0telsX>n}ex95Xdd`0BoxDC0v5Z`kHGNyW#qj5DyTk_u)#`z| z$ChDj)*tmAq7-E+Y?+8=6gAu@HSHq|G3pV^mDDcG;kWMe>tkag19eez8Ab-fEF%MJ z^)hi(N{K^-b8N{>sgS~t5h2;z_-RGl7hSwW6_Vx(8m^%T%+GiJxqV)h7k_0BzQrr2 z>Amu$7@rk?edH0DUMrV51WOE6;S&C4dSWQ>_G9gW^HKy97Qz*))FkI(A&OQN`@YHL z%4ni>bSx97fqsrs3m*C0njLrPm3gWmcV{Vp)a26SVHiQ1?4rR-bDp_MOHS7aZU$xhvRq2rV>fWrV!?YDW*9= zvr-?+t1d}f^~MLRpU?DA1F`1?BHj4!@*z)ybO?P!x1O@X7ep>*%x3HN~F*RM|;RwfcIc)!FYCpk`VM2wU z#HoW*X_{Zb%xUD;*`8Eu@Jhv5OsO|45UER39N1-WK->ipfG*7ksB7)U;5Z+1xJExK z2p8y-*lwUbC0#KySXTa_d|WhG+$^|)u&%}Nm;Yv9cluf+O^G?@1uqL%0|^h7zpc9Z ztEN#jlJe2-+Zu0Sd=)E|)u()NWDPE0;s3)OmFhS-MB69XH?qnt)yeB4U;L?=aM4`n z&!2ZsP^~kIDgBCyp;(68f9C{M2#Kn(83HVey3L_`eO^_Af@hMoO;Ljdnk^EJf06HS6ldH=L>iz5u7-@#(7@aOptR| z=-`|kq;fmx?utzngCAc?9MXhw&F~PrD2?|Th?=E{wgOvQN8K%>Gs?Nk3ICKb&@aOs z3t~liP_Hx^l^I9Sg6B`u{40O}5*H1xZ~YOZg47^i(BnSwyl31ftDIbdfMwUBt4LSE z1T=n|4mbY&V9Y5EhtQVDGFGPFiv*-5P&Ti~oTi8)_?1KG z5%2C3?M;)G7B)dcsFfT&Asvg=owSi=A0qP!od2PvYKwiNF07!|tR(TCI7DjxTEKRoNxw$!^ zfQj;qBm<{kP#rJt{XB?PiwbGNo%ji zBSMriUH(BDDdm4fj~`Px)f@>g6q*su<%2syLxH!@p zMI9-`RtI<>ry5cb`VJzGpc}ZLKam$gn!VT8i0E|_>ouyV3z{HXQy&h`FV6GDd%pG9 zA9eA!lBz5D@ZFsj)^T4<=Di9N-jG1g{-h{sU6>{m#M&&YDh<-PRKor2`Y5~t@uGtY zITa7iqsuLp=M^v70NSO`W83QoUSeI3d`yluH`FJK=P2KUk)d=A?W*@x)SrP??u}1r z!a()bx?qY@HUiQE5q!A+nPCe5%~i(5=Il}B%`&`{sxEr7Wl_$gQ~5>zTrXedj9xht z%a)z4N7~D`n<1NetOlOyW9JFL%hwoJEn^#MPfHrtbk<_WiUDq{BUvAWS6r*DHv-M5 zHwgB?o8fj{CyGhW*WEC^KZL-m)OKoI#Ix}-)jaL$lI9_(VN#0*`(R}sOMy`OR-j}8 zOTkNnW7AO_L>-&3awJ1e5G298kR)qOb}6k_ITy+i8-!=ehe5?67xR@1kjfM-`+|GYMElc)(HMJB2~?y-wVR!}tyY!~oi zoJ0cVU=wAMbal5JQ$`B8I5ZD&IAj@o%dO8k;8x!so7ZPmT4*Z)YifTU{DCp+noBmt zy_Jlpd*lz+$*9w~L1Q~~gcNpq4x@|2Wqx3C0QU?-PJ(@zD!6{pc$r$OJ{p19+}->Qjr>Sx}N+y&0I zc*RxwLExArJ@Vh1A}OW6zcb`t3 zF1!Rgh`eT09X6C(Ly*8)zJIGnTpAKbP2?>2Gg(b{t^*no0*KICUYQf}N4yp5Ap7(6 zM;bq}Sp%cWY$&y;D}K_kZ6~}_N)Kk!!0qKJY4b!6U_- zR`N!=&Gy|w)k|MThU_c3V;h-w1AZmdX9NDQXBj)s6>D`QL7C-i9qm+3ayyqi)o_4% zSdkI!wp=x6r8ocdpOz}=)*`V(Qla+aMgYjlkeAtMH0Lr%>{;Y$g+xM9KG%Xlo0-Sw(;_x zgrd4Azt~)Vt9@Tacj$%DN_I@&s^H8Ecs8Y59ScOpRF)@5&J7BQhj-luoY=E*p|gJiblGWSZIcC40-%u4Pif1PTB1j-<|;fYO4PZAD6c-8B7^8!55og(o3^m9k(zTSpi{w03+%rW=z5u1h@Y76Q@vLa%V8CoA z$T@^&XMYi$_yg0d((?X9%qa~THxyDHxacj^GVu_y*J{B%oJ5`sL{rNN_~Px=F6Pz$ zn?&pdE@PlABiij_>#@WJEFPku4AzKjPI!y^W^V4YrIVza8#t00N8XgIzDI%A+b9t@ zpE&C0it!$=k$-`PjZ0sykigLS7 zui+f&@rf*pRlTPkNx}BIGoRmKGGmn#u;!r5mH5kqJc2Lr%(!Lh+Z28<0MG^uj39aK__vUcZ9b+i;fFD#miCn#Gs#y@?uPl%pa@@oH15aAHmk9wK`*= z*6KLNj^JC-jtD&tme1cZr{Bf6e-KSV8%AqNP;Vf}vC~xp7%W@2n4pwv=}gbU%j+S8 z(DRu(ttlchkJK^nS+GvodSq$wN>>NIM!Csbt1$$-Y5#HhmX;u${8UQ`Jk;v-GkR}* z+PWvqTAc1VV|%hGs*aR4vfXSm$E$N{Zw334oj7Iv598aFI2{{&apNw!``^u;*`c-s zVDpYc9&}MJpSpkHhl%#Lfis7;GDiMPFvwLqz-D9o4)w(m*(z<@=w?jEBwOioPzhYx zbHU`^fsFNdNeQi~NpdWkOjEO~m$)8WJ&Tpi!nDHcS-o^mc^y_xI>Pj;o9^9%OO8jp zM5bqx@1__#bbX_Hv3|_@MZxuRdV6qP9cKfZe#=k%v6>2#(kUG+rc0LQk~l_*yu|{2 zt~8tN;O@e_UPo|O?d74zGUA z*x%6kI(0~aAVLh;vQYD$**1!{7_rA4*dEyTK=P<81c&%SfR5ziTQ%AIv#@)xt~hL% zf>sKs=-77EStnnfwx7EmN>5KFDSp zbk!8Pl5*k25pF{OH_l1!_ddkIHoH zH?VraeNi7XUHrF!W{y;fs)6t?x7^4sroKwR0I!|*H-b%PxOU97RE4Ov0NmuIR*)--qAwI;|5gfCby^nsUiS>~gC4V9oR z0Uli$Ym`nkt&6newy|`t#sE9OdfEP#V;FT-2cFS4VaWN> zcj9Osp9^`+*XH%~cIa}-5lbB%Ig^$bx6sI|p-Hj@Gyhfc!n_iM$Z<>h^QpG7`r}V>qIFU&6Pg2+@~U>?gmg>o>|@yJPWq} z1}nXEi%>yKML5`%WSKERumzjR+B4vnhW@UWl8`jFONe%k77{0UYN5=C#3g?$L>}}0 z#Cj>g3#B($x2j;E)!E?ZQ?AuJop-FShr5cUazNWdBKO8Ug^4ow71{J!J9|BU3ME0P%HZXgZN)dS#n zv;mzbM(VK`Lqp&zk{gAa4w`Q@$tKua$sYj>%JPRZQCyLQ5S$3Q%~zRWCs00`5R>=E3@EAVA-Mn<^c8g zW+;B1@mnLIokE$}+=&}~cWQj`;oY|e0(4*NX1~S@>(cV}aO%A&PZ!_wOstL+@+Ah< zpZ;|7qz(Us45@{+!y&WXy5(P$FQB{ekcMEm3h=X$c_UFq$3=7cZcY$NGCHiK?;EJw zVJizfLY$~^Ja5iRv++}&NyFF8)Qzq6Yt9kJOPYG-1|`VZlp`Ws=(!m!OV!}{`?3}7ji7x8Z@djQJ^$5IG?G&jo zu`vnv)G8xDKh7x>Mvo$|GGW%?5wAW<2e$BVE+DF|Gdt*(`bnEo2yG4Er+1f?&gmQO zCdcm787p0i#%N8N)#S2$;E5wAsr(RnaJ8!;8skK$A3Hl+T5SLh?H4l0PL;qfIce{c zU!&X}-KkfV13fKtS}KBsEi@Slx5YQd5^j#y8ucmU(H?h65yp84?F*wDU%t$ROr;0iY8-lU} zn;_$ao-A1)&pY>xyEn+#vhxNTcBW|=U+Sd5WhzK!5?!} z%Q=3@c5NMHv4Dot0Bigr$EuhYReU((J-Y1gL-;bQzXdIRqAb6&;Ne>h&byd(RZvch zeC#~oy#i!mdl+VHfa3)4gy}IsNfRSuZ-8`=h4ADv#`R#mZB{;i(*nfW0QZwd<-bgo zOg-fHr(M*gMw;%N9!NG`BVNir}RL7jO{@6@8T~5^&~%pnlq?A0jN*QO%`NEE9~eze3EOO zyuHyclDPR@WO35Fit0lkr+MqhMq3CZ!t0@QDH{uRQ&sRIlMg+5;IY)yzWMidxgiLhsHoV2!*KVA%kQ#}zD58!F8_V0g zx%aNdMx0}pa9|JC<0B$wnygs{qZ~1J-)VuTO6CVh>Fv5-qr$(9=)+}4fNlxWird-H zPRb}6?j8cOZ{$*N5jQF%xGh;gn5Z)iQCpArtTe3hxW@{kD60?A&^#hxSPpWS$|dXWV`>E(v9>x0XW5k?Pr;;#a)|~TzGZ`- z6{*Z3M&GP~RkA%w!W2~Z+dblo{y1z51pLtfLAcA5v!_2v+NVWuor zPKujcZB)M*MWpQG6Bw%e4RTaah;b#UHJID6{MF%Do%&ddfoWSl9z)eS;6c$UEqxHn zC;p{rn><(c+|=Teov}hQ$;=*#z9!_~+GAz|nrt?zYM%l^sYx&Ok|gZXQfCfCBD2ht zG+YvFBYjdK=tL1DCRewTbM~f$V;l zAsoHIXITkhQ_+ibJlgkBAwu>KuRcLejB~JOW}O{@jl^AvCxPMlrWizX9ZuxDF7IEd zEVX}tq5U)!_~sZ#zI>zlHqq~_m)!HIAw!8Fi}u>o=t(r`AErTO6^8UZYv9nNIc>CX(RZMtM1=^b ziKVa5@!n$56;Yj_c0_R9*qctnO;7=q} zlpG6y4^CV;FQW+%?ZGZqwrZR+kq|2xR zzL<3Hsn*3_jGaFH>~+1Z%Tz$?$GAC|2ya)`YS`m=Avwcj0Zw~S{dbcbRgjX@orpX^Rxxga5h$u`4`51UP13~D!w*4tE$)!<|l zOauH3Ij(B_5NsfTLRPEI{NXRy1R7tQgr)d+W;<-n=+xs=JDbzwQY_z#a|EV`vuM^{ z(_z4^`$sb~Bg((DaP2hBU^jq+`d!$r$Sp~#WEX%WAsIl0cx0TYeCU?`Cu}Au-hiL zCe*r}_{N0l#J`@R=(Pttjo)#d>dz%jd<}qRvs+MaCvxK$wW=@fL!JE}jZ{U4elVI= zOI_i#!Ahh$zUg#VpR;yxVVzo#>UgDpi-waMOyC%(?3xa!6Db+q#-*$VAwF3#O4Ydv zqMhP^jO$Ni9lK?@EU0b!@@<##QpaPP>tn1MFWe}n6rs2;H?w~)Ias1hLab^% zB8p-e5Jn3PW~B9YX1Vb%g$PzFr=%o4T4KGf^zpX|=-s4YSCp7TSW*win`2X{sS|LS zImz0bVrrdmHeW?|)caxwD?I(}0;VDn#ps-)x&$-9V3mv~9N6odTDGLQqYv-S0$Qe92YecO8TYsyy9 zJyctEP8m_t0cb%XtLF(wdqt@Y&_``pDIWHLBd-cZn8-nx(`Wdx_ja?b>fN?7N+$jJ za|oV0jx`ZjlCUsXZ+DMpz$7HgpvShah$Lu;n&<0BRzi z9uOC%Av5SieyTR2uCc6I4*g+d3~zOcbc*-_o03HEov;?ArTQ~(#X#cG6Kx&I)Ky;) z+y35a*ym(?_FWV&K8!k@HjVt-Q|E}K@p&XXrw9y=2qm^Ym^xhC9In`6aYj7HnLB3O zo6o08e*Ss>&`3mF#F()@)tfq}sDVGKp054CBEx?K*tT?VnXltOGXECNQ&WjLHJtuX z&r45<0f4NEv8_z?TMo?55kR^}4r3GcU6Ecy3{y%c

      `uH$5;0_ZJX;(^d_OBM`2OkiO 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 diff --git a/io.openems.wrapper/jar/Java-WebSocket-1.3.7.jar b/io.openems.wrapper/jar/Java-WebSocket-1.3.7.jar deleted file mode 100644 index e6227a4a4d66dc76477a16479aead833fe0d8f82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111418 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%0D{Quu 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 Oe2pP)7PliucKrvPTmS9= diff --git a/io.openems.wrapper/websocket.bnd b/io.openems.wrapper/websocket.bnd new file mode 100644 index 00000000000..39d0524f20b --- /dev/null +++ b/io.openems.wrapper/websocket.bnd @@ -0,0 +1,16 @@ +-dsannotations: * +-metatypeannotations: * +Service-Component: * + +Bundle-DocURL: https://github.com/TooTallNate/Java-WebSocket +Bundle-License: https://github.com/TooTallNate/Java-WebSocket/blob/8ef67b46ecc927d5521849dcc2d85d10f9789c20/LICENSE +Bundle-Description: This repository contains a barebones WebSocket server and client implementation written \ + in 100% Java. The underlying classes are implemented using the Java ServerSocketChannel and SocketChannel \ + classes, which allows for a non-blocking event-driven model (similar to the WebSocket API for web browsers). \ + Implemented WebSocket protocol versions are: Hixie 75, Hixie 76, Hybi 10, and Hybi 17 +Bundle-Version: 1.3.7 + +Include-Resource: @Java-WebSocket-1.3.7.jar + +-exportcontents: org.java_websocket, org.java_websocket.drafts, org.java_websocket.exceptions, org.java_websocket.handshake, org.java_websocket.server, org.java_websocket.framing, org.java_websocket.extensions, org.java_websocket.protocols +-sources: false From 39de41387eae59c788326e962710d1b4a516712a Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 23 Feb 2018 10:16:41 +0100 Subject: [PATCH 094/156] Handle currentData subscription --- doc/readme.md | 6 +- .../backend/metadata/api/MetadataService.java | 2 +- .../openems/backend/metadata/odoo/Odoo.java | 20 +- .../backend/timedata/api/TimedataService.java | 9 +- .../io.openems.backend.timedata.influx.bndrun | 6 +- .../backend/timedata/influx/Influx.java | 69 +------ .../timedata/influx/InfluxdbUtils.java | 57 ++++++ .../bnd.bnd | 3 +- .../provider/BackendCurrentDataWorker.java | 41 +++++ .../impl/provider/UiWebsocket.java | 8 +- .../impl/provider/UiWebsocketServer.java | 174 ++++++++++++------ .../impl/provider/WebsocketData.java | 10 + .../common/websocket/CurrentDataWorker.java | 13 +- .../common/websocket/DefaultMessages.java | 21 ++- ui/src/app/shared/device/device.ts | 23 ++- ui/src/app/shared/service/defaultmessages.ts | 9 +- ui/src/app/shared/service/defaulttypes.ts | 3 +- 17 files changed, 302 insertions(+), 172 deletions(-) create mode 100644 io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java diff --git a/doc/readme.md b/doc/readme.md index 8665656b572..ea4213fb90a 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -149,8 +149,8 @@ For 'unsubscribe' the channels object is empty. ``` { - id: [string], - device: string, + messageId: UUID, + edgeId: number, currentData: { mode: "subscribe", channels: { @@ -164,7 +164,7 @@ For 'unsubscribe' the channels object is empty. ``` { - device?: string, + messageId: UUID, currentData: {[{ channel: string, value: any diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java index 9b322a47574..e2060ae9805 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java @@ -9,7 +9,7 @@ @ProviderType public interface MetadataService { - public abstract Optional getUserWithSession(String sessionId) throws OpenemsException; + public abstract User getUserWithSession(String sessionId) throws OpenemsException; public abstract int[] getEdgeIdsForApikey(String apikey); diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 9c4b60e0f0f..1614458a531 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -91,7 +91,7 @@ void deactivate() { * @throws OpenemsException */ @Override - public Optional getUserWithSession(String sessionId) { + public User getUserWithSession(String sessionId) throws OpenemsException { HttpURLConnection connection = null; try { // send request to Odoo @@ -118,7 +118,13 @@ public Optional getUserWithSession(String sessionId) { if (j.has("error")) { JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); String errorMessage = JsonUtils.getAsString(jError, "message"); - throw new OpenemsException("Odoo replied with error: " + errorMessage); + switch (errorMessage) { + case "Odoo Session Expired": + // TODO handle exception + throw new OpenemsException("Odoo Session Expired"); + default: + throw new OpenemsException("Odoo replied with error: " + errorMessage); + } } if (j.has("result")) { @@ -143,19 +149,19 @@ public Optional getUserWithSession(String sessionId) { synchronized (this.users) { this.users.put(user.getId(), user); } - return Optional.of(user); + return user; } } + // TODO handle exception + throw new OpenemsException("No matching user found"); } catch (IOException e) { - log.error("IOException while reading from Odoo: " + e.getMessage()); - } catch (OpenemsException e) { - log.error("OpenemsException while reading from Odoo: " + e.getMessage()); + // TODO handle exception + throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); } finally { if (connection != null) { connection.disconnect(); } } - return Optional.empty(); } @Override diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index 3fa74209f04..68a20920c72 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -31,7 +31,14 @@ public interface TimedataService { */ public void write(int edgeId, JsonObject jData) throws OpenemsException; - public Optional getChannelValue(int deviceId, ChannelAddress channelAddress); + /** + * Gets the latest value for the given ChannelAddress + * + * @param edgeId + * @param channelAddress + * @return + */ + public Optional getChannelValue(int edgeId, ChannelAddress channelAddress); /** * Queries the database and returns a JsonArray of the form diff --git a/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun b/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun index 19406cddf17..4e155ea00d0 100644 --- a/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun +++ b/io.openems.backend.timedata.influx.provider/io.openems.backend.timedata.influx.bndrun @@ -30,6 +30,6 @@ JPM-Command: provider slf4j.api;version='[1.8.0,1.8.1)',\ com.google.guava;version='[19.0.0,19.0.1)',\ org.apache.servicemix.bundles.influxdb-java;version='[2.3.0,2.3.1)',\ - org.apache.servicemix.bundles.okhttp;version='[2.2.0,2.2.1)',\ - org.apache.servicemix.bundles.okio;version='[1.2.0,1.2.1)',\ - org.apache.servicemix.bundles.retrofit;version='[1.9.0,1.9.1)' + org.apache.servicemix.bundles.retrofit;version='[1.9.0,1.9.1)',\ + org.apache.servicemix.bundles.okhttp;version='[2.7.5,2.7.6)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)' diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index da6f114da39..4351fce0f41 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -1,7 +1,5 @@ package io.openems.backend.timedata.influx; -import java.text.NumberFormat; -import java.text.ParseException; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; @@ -31,7 +29,6 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; import io.openems.backend.metadata.api.Edge; import io.openems.backend.metadata.api.MetadataService; @@ -88,15 +85,13 @@ void activate(Config config) throws OpenemsException { this.username = config.username(); this.password = config.password(); this.measurement = config.measurement(); - this.getInfluxDbConnection(); } @Deactivate void deactivate() { log.debug("Deactivate InfluxDB"); if (this.influxDbOpt.isPresent()) { -// this.influxDbOpt.get().close(); - //TODO + // TODO this works only with a more recent version of Influxdb-Java this.influxDbOpt.get().close(); } } @@ -200,55 +195,7 @@ private void writeDataToOldMiniMonitoring(Edge edge, int influxId, TreeBasedTabl influxDB.write(batchPoints); } - /** - * Add value to Influx Builder in the correct data format - * - * @param builder - * @param channel - * @param value - * @return - */ - private Optional parseValue(String channel, Object value) { - if (value == null) { - return Optional.empty(); - } - // convert JsonElement - if (value instanceof JsonElement) { - JsonElement jValueElement = (JsonElement) value; - if (jValueElement.isJsonPrimitive()) { - JsonPrimitive jValue = jValueElement.getAsJsonPrimitive(); - if (jValue.isNumber()) { - try { - // Avoid GSONs LazilyParsedNumber - value = NumberFormat.getInstance().parse(jValue.toString()); - } catch (ParseException e) { - log.error("Unable to parse Number: " + e.getMessage()); - value = jValue.getAsNumber(); - } - } else if (jValue.isBoolean()) { - value = jValue.getAsBoolean(); - } else if (jValue.isString()) { - value = jValue.getAsString(); - } - } - } - if (value instanceof Number) { - Number numberValue = (Number) value; - if (numberValue instanceof Integer) { - return Optional.of(numberValue.intValue()); - } else if (numberValue instanceof Double) { - return Optional.of(numberValue.doubleValue()); - } else { - return Optional.of(numberValue); - } - } else if (value instanceof Boolean) { - return Optional.of((Boolean) value); - } else if (value instanceof String) { - return Optional.of((String) value); - } - log.warn("Unknown type of value [" + value + "] channel [" + channel + "]. This should never happen."); - return Optional.empty(); - } + @Override public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, @@ -259,8 +206,8 @@ public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime } @Override - public Optional getChannelValue(int deviceId, ChannelAddress channelAddress) { - DeviceCache deviceCache = this.deviceCacheMap.get(deviceId); + public Optional getChannelValue(int edgeId, ChannelAddress channelAddress) { + DeviceCache deviceCache = this.deviceCacheMap.get(edgeId); if (deviceCache != null) { return deviceCache.getChannelValueOpt(channelAddress.toString()); } else { @@ -286,10 +233,10 @@ public void write(int edgeId, JsonObject jData) throws OpenemsException { TreeBasedTable data = TreeBasedTable.create(); // get existing or create new DeviceCache - DeviceCache deviceCache = this.deviceCacheMap.get(influxId); + DeviceCache deviceCache = this.deviceCacheMap.get(edgeId); if (deviceCache == null) { deviceCache = new DeviceCache(); - this.deviceCacheMap.put(influxId, deviceCache); + this.deviceCacheMap.put(edgeId, deviceCache); } // Sort incoming data by timestamp @@ -348,7 +295,7 @@ public void write(int edgeId, JsonObject jData) throws OpenemsException { // add incoming data to cache (this replaces already existing cache values) for (Entry channelEntry : jChannels.entrySet()) { String channel = channelEntry.getKey(); - Optional valueOpt = this.parseValue(channel, channelEntry.getValue()); + Optional valueOpt = InfluxdbUtils.parseValue(channel, channelEntry.getValue()); if (valueOpt.isPresent()) { Object value = valueOpt.get(); deviceCache.putToChannelCache(channel, value); @@ -359,7 +306,7 @@ public void write(int edgeId, JsonObject jData) throws OpenemsException { // add incoming data to write data for (Entry channelEntry : jChannels.entrySet()) { String channel = channelEntry.getKey(); - Optional valueOpt = this.parseValue(channel, channelEntry.getValue()); + Optional valueOpt = InfluxdbUtils.parseValue(channel, channelEntry.getValue()); if (valueOpt.isPresent()) { Object value = valueOpt.get(); data.put(timestamp, channel, value); diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java index f55a24290aa..52c310bcb6b 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java @@ -1,5 +1,7 @@ package io.openems.backend.timedata.influx; +import java.text.NumberFormat; +import java.text.ParseException; import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -16,11 +18,14 @@ import org.influxdb.dto.QueryResult; import org.influxdb.dto.QueryResult.Result; import org.influxdb.dto.QueryResult.Series; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @@ -28,6 +33,8 @@ public class InfluxdbUtils { + private final static Logger log = LoggerFactory.getLogger(InfluxdbUtils.class); + public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional deviceId, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { // Prepare query string @@ -103,6 +110,56 @@ public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Op return j; } + /** + * Add value to Influx Builder in the correct data format + * + * @param builder + * @param channel + * @param value + * @return + */ + protected static Optional parseValue(String channel, Object value) { + if (value == null) { + return Optional.empty(); + } + // convert JsonElement + if (value instanceof JsonElement) { + JsonElement jValueElement = (JsonElement) value; + if (jValueElement.isJsonPrimitive()) { + JsonPrimitive jValue = jValueElement.getAsJsonPrimitive(); + if (jValue.isNumber()) { + try { + // Avoid GSONs LazilyParsedNumber + value = NumberFormat.getInstance().parse(jValue.toString()); + } catch (ParseException e) { + log.error("Unable to parse Number: " + e.getMessage()); + value = jValue.getAsNumber(); + } + } else if (jValue.isBoolean()) { + value = jValue.getAsBoolean(); + } else if (jValue.isString()) { + value = jValue.getAsString(); + } + } + } + if (value instanceof Number) { + Number numberValue = (Number) value; + if (numberValue instanceof Integer) { + return Optional.of(numberValue.intValue()); + } else if (numberValue instanceof Double) { + return Optional.of(numberValue.doubleValue()); + } else { + return Optional.of(numberValue); + } + } else if (value instanceof Boolean) { + return Optional.of((Boolean) value); + } else if (value instanceof String) { + return Optional.of((String) value); + } + log.warn("Unknown type of value [" + value + "] channel [" + channel + "]. This should never happen."); + return Optional.empty(); + } + // private static JsonObject querykWh(InfluxDB influxdb, String database, // Optional fems, // ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int diff --git a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd index f8444e0928a..97c3d7a596c 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd @@ -18,7 +18,8 @@ Private-Package: io.openems.backend.uiwebsocket.impl.provider io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ io.openems.backend.uiwebsocket.api;version=latest,\ - io.openems.backend.edgewebsocket.api;version=latest + io.openems.backend.edgewebsocket.api;version=latest,\ + io.openems.backend.timedata.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java new file mode 100644 index 00000000000..16c271f631b --- /dev/null +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java @@ -0,0 +1,41 @@ +package io.openems.backend.uiwebsocket.impl.provider; + +import java.util.Optional; + +import org.java_websocket.WebSocket; + +import com.google.common.collect.HashMultimap; +import com.google.gson.JsonElement; + +import io.openems.common.exceptions.NotImplementedException; +import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.CurrentDataWorker; + +public class BackendCurrentDataWorker extends CurrentDataWorker { + + private final UiWebsocketServer parent; + private final int edgeId; + + public BackendCurrentDataWorker(UiWebsocketServer parent, WebSocket websocket, String messageId, int edgeId, + HashMultimap channels) { + super(websocket, messageId, channels); + this.parent = parent; + this.edgeId = edgeId; + } + + @Override + protected Optional getChannelValue(ChannelAddress channelAddress) { + Optional channelCacheOpt = this.parent.parent.timeDataService.getChannelValue(this.edgeId, + channelAddress); + if (channelCacheOpt.isPresent()) { + try { + return Optional.ofNullable(JsonUtils.getAsJsonElement(channelCacheOpt.get())); + } catch (NotImplementedException e) { + return Optional.empty(); + } + } else { + return Optional.empty(); + } + } +} diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 1a70f9c1fa8..27a20af3cf7 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -16,6 +16,7 @@ import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.timedata.api.TimedataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; import org.osgi.service.metatype.annotations.Designate; @@ -28,11 +29,14 @@ public class UiWebsocket implements UiWebsocketService { private UiWebsocketServer server = null; - @Reference - protected MetadataService metadataService; + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected volatile MetadataService metadataService; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) private volatile EdgeWebsocketService edgeWebsocketService; + + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected volatile TimedataService timeDataService; @ObjectClassDefinition @interface Config { diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index e2588dd0785..a751f40acf3 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -12,7 +12,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.HashMultimap; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.backend.metadata.api.Edge; @@ -20,14 +22,15 @@ import io.openems.backend.metadata.api.User; import io.openems.common.exceptions.OpenemsException; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; import io.openems.common.websocket.AbstractWebsocketServer; import io.openems.common.websocket.DefaultMessages; import io.openems.common.websocket.WebSocketUtils; public class UiWebsocketServer extends AbstractWebsocketServer { + protected final UiWebsocket parent; private final Logger log = LoggerFactory.getLogger(UiWebsocketServer.class); - private final UiWebsocket parent; private final Map websocketsMap = new HashMap<>(); public UiWebsocketServer(UiWebsocket parent, int port) { @@ -38,57 +41,50 @@ public UiWebsocketServer(UiWebsocket parent, int port) { @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { String error = ""; - Optional userOpt = Optional.empty(); + User user; // login using session_id from the cookie Optional sessionIdOpt = getSessionIdFromHandshake(handshake); - if (!sessionIdOpt.isPresent()) { - error = "Session-ID is missing in handshake"; - } else { - try { - userOpt = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); - // TODO fix bug in Odoo that is not reliably returning all configured devices - } catch (OpenemsException e) { - error = e.getMessage(); + try { + if (!sessionIdOpt.isPresent()) { + throw new OpenemsException("Session-ID is missing in handshake"); } - } - - if (!error.isEmpty()) { + user = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); + // TODO fix bug in Odoo that is not reliably returning all configured devices + } catch (OpenemsException e) { // send connection failed to browser - this.send(websocket, DefaultMessages.browserConnectionFailedReply()); + this.send(websocket, DefaultMessages.uiConnectionFailedReply()); log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + error + "]."); websocket.closeConnection(CloseFrame.REFUSE, error); + return; + } + + UUID uuid = UUID.randomUUID(); + synchronized (this.websocketsMap) { + // add websocket to local cache + this.websocketsMap.put(uuid, websocket); + } + // store userId together with the websocket + websocket.setAttachment(new WebsocketData(user.getId(), uuid)); - } else if (userOpt.isPresent()) { - User user = userOpt.get(); - - UUID uuid = UUID.randomUUID(); - synchronized (this.websocketsMap) { - // add websocket to local cache - this.websocketsMap.put(uuid, websocket); - } - // store userId together with the websocket - websocket.setAttachment(new WebsocketData(user.getId(), uuid)); - - // send connection successful to browser - JsonArray jEdges = new JsonArray(); - for (Entry edgeRole : user.getEdgeRoles().entrySet()) { - int edgeId = edgeRole.getKey(); - Role role = edgeRole.getValue(); - Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); - if (!edgeOpt.isPresent()) { - log.warn("Unable to find Edge [ID:" + edgeId + "]"); - } else { - JsonObject jEdge = edgeOpt.get().toJsonObject(); - jEdge.addProperty("role", role.toString()); - jEdges.add(jEdge); - } + // send connection successful to browser + JsonArray jEdges = new JsonArray(); + for (Entry edgeRole : user.getEdgeRoles().entrySet()) { + int edgeId = edgeRole.getKey(); + Role role = edgeRole.getValue(); + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (!edgeOpt.isPresent()) { + log.warn("Unable to find Edge [ID:" + edgeId + "]"); + } else { + JsonObject jEdge = edgeOpt.get().toJsonObject(); + jEdge.addProperty("role", role.toString()); + jEdges.add(jEdge); } - JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply("" /* TODO empty token? */, - Optional.empty(), jEdges); - WebSocketUtils.send(websocket, jReply); - log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); } + JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply("" /* TODO empty token? */, + Optional.empty(), jEdges); + WebSocketUtils.send(websocket, jReply); + log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); } @Override @@ -106,6 +102,11 @@ protected void _onClose(WebSocket websocket) { } else { log.info("User [ID:" + data.getUserId() + "] disconnected."); } + // stop CurrentDataWorker + Optional currentDataWorkerOpt = data.getCurrentDataWorker(); + if (currentDataWorkerOpt.isPresent()) { + currentDataWorkerOpt.get().dispose(); + } // remove websocket from local cache synchronized (this.websocketsMap) { this.websocketsMap.remove(data.getUuid()); @@ -117,7 +118,13 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { // get current User WebsocketData data = websocket.getAttachment(); int userId = data.getUserId(); - + Optional userOpt = this.parent.metadataService.getUser(userId); + if (!userOpt.isPresent()) { + // TODO Error user not found + return; + } + User user = userOpt.get(); + // get MessageId from message Optional messageIdOpt = JsonUtils.getAsOptionalString(jMessage, "messageId"); @@ -131,12 +138,19 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * verify that User is allowed to access Edge */ - Optional userOpt = this.parent.metadataService.getUser(userId); - if (!userOpt.isPresent() || !(userOpt.get().getEdgeRole(edgeId).isPresent())) { + if (!user.getEdgeRole(edgeId).isPresent()) { // TODO Error Access denied return; } + // get Edge + Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); + if (!edgeOpt.isPresent()) { + // TODO Error unable to find Edge + return; + } + Edge edge = edgeOpt.get(); + /* * TODO Query historic data */ @@ -159,21 +173,13 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * TODO Subscribe to currentData */ - // if (jMessage.has("currentData")) { - // JsonObject jCurrentData; - // try { - // jCurrentData = JsonUtils.getAsJsonObject(jMessage, "currentData"); - // log.info("User [" + session.getData().getUserName() + "] subscribed to - // current data for device [" - // + deviceName + "]: " + StringUtils.toShortString(jCurrentData, 50)); - // JsonArray jMessageId = jMessageIdOpt.get(); - // int deviceId = deviceIdOpt.get(); - // this.currentData(session, websocket, jCurrentData, jMessageId, deviceName, - // deviceId); - // } catch (OpenemsException e) { - // log.error(e.getMessage()); - // } - // } + Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); + if (jCurrentDataOpt.isPresent()) { + JsonObject jCurrentData = jCurrentDataOpt.get(); + log.info("User [" + user.getName() + "] subscribed to current data for device [" + edge.getName() + + "]: " + StringUtils.toShortString(jCurrentData, 50)); + this.currentData(websocket, data, messageId, edgeId, jCurrentData); + } /* * Serve "Config -> Query" from cache @@ -186,7 +192,6 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * Query current config */ - Edge edge = this.parent.metadataService.getEdge(edgeId).get(); JsonObject jReply = DefaultMessages.configQueryReply(messageId, edge.getConfig()); WebSocketUtils.send(websocket, jReply); break; @@ -208,4 +213,51 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { } } } + + /** + * Handle current data subscriptions + * + * @param j + */ + private synchronized void currentData(WebSocket websocket, WebsocketData data, String messageId, int edgeId, + JsonObject jCurrentData) { + try { + String mode = JsonUtils.getAsString(jCurrentData, "mode"); + + if (mode.equals("subscribe")) { + /* + * Subscribe to channels + */ + + // remove old worker if it existed + Optional workerOpt = data.getCurrentDataWorker(); + if (workerOpt.isPresent()) { + data.setCurrentDataWorker(null); + workerOpt.get().dispose(); + } + + // parse subscribed channels + HashMultimap channels = HashMultimap.create(); + JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); + for (Entry entry : jSubscribeChannels.entrySet()) { + String thing = entry.getKey(); + JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement jChannel : jChannels) { + String channel = JsonUtils.getAsString(jChannel); + channels.put(thing, channel); + } + } + if (!channels.isEmpty()) { + // create new worker + BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, messageId, edgeId, + channels); + data.setCurrentDataWorker(worker); + } + } + } catch (OpenemsException e) { + // TODO handle exception + log.warn(e.getMessage()); + } + } + } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java index 74567f461d2..d56a6bd47e8 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/WebsocketData.java @@ -1,10 +1,12 @@ package io.openems.backend.uiwebsocket.impl.provider; +import java.util.Optional; import java.util.UUID; public class WebsocketData { private final int userId; private final UUID uuid; + private Optional currentDataWorker = Optional.empty(); public WebsocketData(int userId, UUID uuid) { super(); @@ -19,4 +21,12 @@ public int getUserId() { public UUID getUuid() { return uuid; } + + public void setCurrentDataWorker(BackendCurrentDataWorker currentDataWorker) { + this.currentDataWorker = Optional.ofNullable(currentDataWorker); + } + + public Optional getCurrentDataWorker() { + return currentDataWorker; + } } diff --git a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java index 941ea00b169..6ccd8391615 100644 --- a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -8,7 +8,6 @@ import org.java_websocket.WebSocket; import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -33,19 +32,21 @@ public abstract class CurrentDataWorker { */ private final ScheduledFuture future; - public CurrentDataWorker(JsonArray jId, Optional deviceNameOpt, HashMultimap channels) { + private final WebSocket websocket; + + public CurrentDataWorker(WebSocket websocket, String messageId, HashMultimap channels) { + this.websocket = websocket; this.channels = channels; this.future = this.executor.scheduleWithFixedDelay(() -> { /* * This task is executed regularly. Sends data to websocket. */ - Optional wsOpt = this.getWebsocket(); - if (!(wsOpt.isPresent() && wsOpt.get().isOpen())) { + if (!this.websocket.isOpen()) { // disconnected; stop worker this.dispose(); return; } - WebSocketUtils.send(wsOpt.get(), DefaultMessages.currentData(jId, deviceNameOpt, getSubscribedData())); + WebSocketUtils.send(this.websocket, DefaultMessages.currentData(messageId, getSubscribedData())); }, 0, UPDATE_INTERVAL_IN_SECONDS, TimeUnit.SECONDS); } @@ -76,6 +77,4 @@ private JsonObject getSubscribedData() { } protected abstract Optional getChannelValue(ChannelAddress channelAddress); - - protected abstract Optional getWebsocket(); } diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index c14f8e91df5..02952bc1fee 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -14,6 +14,12 @@ public class DefaultMessages { + private static JsonObject newMessage(String messageId) { + JsonObject j = new JsonObject(); + j.addProperty("messageId", messageId); + return j; + } + /** *
         	 *	{
        @@ -66,7 +72,7 @@ public static JsonObject browserConnectionSuccessfulReply(String token, Optional
         	 * @param token
         	 * @return
         	 */
        -	public static JsonObject browserConnectionFailedReply() {
        +	public static JsonObject uiConnectionFailedReply() {
         		JsonObject jAuthenticate = new JsonObject();
         		jAuthenticate.addProperty("mode", "deny");
         		JsonObject j = new JsonObject();
        @@ -162,8 +168,7 @@ public static JsonObject timestampedData(long timestamp, HashMap
         	 *	{
        -	 *		id: [string],
        +	 *		messageId: UUID,
         	 *		currentData: {[{ 
         	 *			channel: string,
         	 *			value: any
        @@ -181,12 +186,8 @@ public static JsonObject configQueryReply(String messageId, JsonObject config) {
         	 * 
         	 * @return
         	 */
        -	public static JsonObject currentData(JsonArray jId, Optional deviceNameOpt, JsonObject jCurrentData) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        -		if(deviceNameOpt.isPresent()) {
        -			j.addProperty("device", deviceNameOpt.get());
        -		}
        +	public static JsonObject currentData(String messageId, JsonObject jCurrentData) {
        +		JsonObject j = newMessage(messageId);
         		j.add("currentData", jCurrentData);
         		return j;
         	}
        diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts
        index 581b1427ad1..70d18543304 100644
        --- a/ui/src/app/shared/device/device.ts
        +++ b/ui/src/app/shared/device/device.ts
        @@ -35,11 +35,6 @@ export class Device {
             private replyStreams: { [messageId: string]: Subject },
             private websocket: Websocket
           ) {
        -    // prepare stream/obersable for currentData
        -    let currentDataStream = replyStreams["currentData"] = new Subject();
        -    this.currentData = currentDataStream
        -      .map(message => message.currentData)
        -      .combineLatest(this.config, (currentData, config) => new CurrentDataAndSummary(currentData, config));
             // prepare stream/obersable for log
             let logStream = replyStreams["log"] = new Subject();
             this.log = logStream
        @@ -98,16 +93,24 @@ export class Device {
             this.websocket.send(value);
           }
         
        +  private sendMessageWithReply(message: DefaultTypes.OutgoingMessage): Subject {
        +    let messageId: string = message.messageId;
        +    this.replyStreams[messageId] = new Subject();
        +    this.send(message);
        +    return this.replyStreams[messageId];
        +  }
        +
           /**
            * Subscribe to current data of specified channels
            */
           public subscribeCurrentData(channels: DefaultTypes.ChannelAddresses): Observable {
        -    // send subscribe
        -    let message = DefaultMessages.currentDataSubscribe(channels);
        -    this.send(message);
             this.subscribeCurrentDataChannels = channels;
        -    // TODO timeout
        -    return this.currentData;
        +    let replyStream = this.sendMessageWithReply(DefaultMessages.currentDataSubscribe(this.id, channels));
        +    let obs = replyStream
        +      .map(message => (message as DefaultMessages.CurrentDataReply).currentData)
        +      .combineLatest(this.config, (currentData, config) => new CurrentDataAndSummary(currentData, config));
        +    // TODO send "unsubscribe" to websocket when nobody is subscribed on this observable anymore
        +    return obs;
           }
         
           /**
        diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts
        index 429ccd70c5c..d403f753944 100644
        --- a/ui/src/app/shared/service/defaultmessages.ts
        +++ b/ui/src/app/shared/service/defaultmessages.ts
        @@ -4,6 +4,7 @@ import { format } from 'date-fns';
         import { DefaultTypes } from './defaulttypes';
         
         export class DefaultMessages {
        +
             public static authenticateLogin(password: string, username?: string) {
                 let m = {
                     authenticate: {
        @@ -36,7 +37,7 @@ export class DefaultMessages {
         
             public static configUpdate(thingId: string, channelId: string, value: any): DefaultTypes.ConfigUpdate {
                 return {
        -            id: [UUID.UUID()],
        +            messageId: UUID.UUID(),
                     config: {
                         mode: "update",
                         thing: thingId,
        @@ -46,10 +47,10 @@ export class DefaultMessages {
                 }
             }
         
        -    public static currentDataSubscribe(channels: DefaultTypes.ChannelAddresses) {
        +    public static currentDataSubscribe(edgeId: number, channels: DefaultTypes.ChannelAddresses) {
                 return {
        -            device: String,
        -            id: ["currentData"],
        +            messageId: UUID.UUID(),
        +            edgeId: edgeId,
                     currentData: {
                         mode: "subscribe",
                         channels: channels
        diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts
        index 7127fd7100d..3623df124c2 100644
        --- a/ui/src/app/shared/service/defaulttypes.ts
        +++ b/ui/src/app/shared/service/defaulttypes.ts
        @@ -1,4 +1,5 @@
         import { Role } from '../type/role'
        +import { UUID } from 'angular2-uuid';
         
         export module DefaultTypes {
         
        @@ -109,7 +110,7 @@ export module DefaultTypes {
           export type LanguageTag = "de" | "en" | "cz" | "nl";
         
           export interface OutgoingMessage {
        -    id: string[]
        +    messageId: string
           }
         
           export interface ConfigUpdate extends OutgoingMessage {
        
        From 22452d7d1a6c8992e99f6579f6b8bda29ea7be9d Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Fri, 23 Feb 2018 11:25:00 +0100
        Subject: [PATCH 095/156] Improve handling of Optionals/Exceptions; query
         historic data
        
        ---
         .../.gitignore                                |   1 +
         .../impl/provider/EdgeWebsocketServer.java    |  94 ++++-----
         .../io/openems/backend/metadata/api/Edge.java |   1 +
         .../metadata/api/EdgeOnlineHandler.java       |  32 +--
         .../backend/metadata/api/MetadataService.java |  17 +-
         .../openems/backend/metadata/odoo/Odoo.java   |   4 +-
         .../backend/timedata/api/TimedataService.java |   4 +-
         .../backend/timedata/influx/Influx.java       | 187 +++++++++---------
         .../timedata/influx/InfluxdbUtils.java        |  18 +-
         .../impl/provider/UiWebsocketServer.java      |  95 ++++++---
         .../common/websocket/DefaultMessages.java     |   7 +-
         ui/src/app/shared/device/device.ts            |  25 +--
         ui/src/app/shared/service/defaultmessages.ts  |  10 +-
         ui/src/app/shared/service/defaulttypes.ts     |   4 +-
         14 files changed, 272 insertions(+), 227 deletions(-)
        
        diff --git a/io.openems.backend.edgewebsocket.api/.gitignore b/io.openems.backend.edgewebsocket.api/.gitignore
        index 90dde36e4ac..6ef3b17057e 100644
        --- a/io.openems.backend.edgewebsocket.api/.gitignore
        +++ b/io.openems.backend.edgewebsocket.api/.gitignore
        @@ -1,3 +1,4 @@
         /bin/
         /bin_test/
         /generated/
        +/generated/
        diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java
        index d7355953f02..596ab1e77b3 100644
        --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java
        +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java
        @@ -82,7 +82,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) {
         
         			// log
         			for (int edgeId : edgeIds) {
        -				Optional edgeOpt = this.parent.metadataService.getEdge(edgeId);
        +				Optional edgeOpt = this.parent.metadataService.getEdgeOpt(edgeId);
         				if (edgeOpt.isPresent()) {
         					log.info("Device [" + edgeOpt.get().getName() + "] connected.");
         				} else {
        @@ -132,11 +132,12 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) {
         		if (jConfigOpt.isPresent()) {
         			JsonObject jConfig = jConfigOpt.get();
         			for (int edgeId : edgeIds) {
        -				Optional edgeOpt = this.parent.metadataService.getEdge(edgeId);
        -				if (!edgeOpt.isPresent()) {
        -					// TODO error unable to find Edge from ID
        -				} else {
        -					edgeOpt.get().setConfig(jConfig);
        +				Edge edge;
        +				try {
        +					edge = this.parent.metadataService.getEdge(edgeId);
        +					edge.setConfig(jConfig);
        +				} catch (OpenemsException e) {
        +					log.warn(e.getMessage());
         				}
         			}
         		}
        @@ -186,7 +187,7 @@ protected void _onClose(WebSocket websocket) {
         
         		// log
         		for (int edgeId : edgeIds) {
        -			Optional edgeOpt = this.parent.metadataService.getEdge(edgeId);
        +			Optional edgeOpt = this.parent.metadataService.getEdgeOpt(edgeId);
         			if (edgeOpt.isPresent()) {
         				log.info("Device [" + edgeOpt.get().getName() + "] disconnected.");
         			} else {
        @@ -201,48 +202,51 @@ public boolean isOnline(int edgeId) {
         
         	private void timedata(int[] edgeIds, JsonObject jTimedata) {
         		for (int edgeId : edgeIds) {
        -			Optional edgeOpt = this.parent.metadataService.getEdge(edgeId);
        -			if (edgeOpt.isPresent()) {
        -				/*
        -				 * write data to timedataService
        -				 */
        -				Edge edge = edgeOpt.get();
        +			Edge edge;
        +			try {
        +				edge = this.parent.metadataService.getEdge(edgeId);
        +			} catch (OpenemsException e) {
        +				log.warn(e.getMessage());
        +				continue;
        +			}
        +			/*
        +			 * write data to timedataService
        +			 */
        +			try {
        +				this.parent.timedataService.write(edgeId, jTimedata);
        +				log.debug("Edge [" + edge.getName() + "] wrote " + jTimedata.entrySet().size() + " timestamps "
        +						+ StringUtils.toShortString(jTimedata, 120));
        +			} catch (Exception e) {
        +				log.error("Unable to write Timedata: ", e);
        +			}
        +			/*
        +			 * set last update timestamps in MetadataService
        +			 */
        +			edge.setLastMessage();
        +
        +			for (Entry jTimedataEntry : jTimedata.entrySet()) {
         				try {
        -					this.parent.timedataService.write(edgeId, jTimedata);
        -					log.debug("Edge [" + edge.getName() + "] wrote " + jTimedata.entrySet().size() + " timestamps "
        -							+ StringUtils.toShortString(jTimedata, 120));
        -				} catch (Exception e) {
        -					log.error("Unable to write Timedata: ", e);
        -				}
        -				/*
        -				 * set last update timestamps in MetadataService
        -				 */
        -				edge.setLastMessage();
        -
        -				for (Entry jTimedataEntry : jTimedata.entrySet()) {
        -					try {
        -						JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue());
        -						// set Odoo last update timestamp only for those channels
        -						for (String channel : jChannels.keySet()) {
        -							if (channel.endsWith("ActivePower")
        -									|| channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2")
        -											| channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) {
        -								edge.setLastUpdate();
        -							}
        +					JsonObject jChannels = JsonUtils.getAsJsonObject(jTimedataEntry.getValue());
        +					// set Odoo last update timestamp only for those channels
        +					for (String channel : jChannels.keySet()) {
        +						if (channel.endsWith("ActivePower")
        +								|| channel.endsWith("ActivePowerL1") | channel.endsWith("ActivePowerL2")
        +										| channel.endsWith("ActivePowerL3") | channel.endsWith("Soc")) {
        +							edge.setLastUpdate();
         						}
        +					}
         
        -						// set specific Odoo values
        -						if (jChannels.has("ess0/Soc")) {
        -							int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt();
        -							edge.setSoc(soc);
        -						}
        -						if (jChannels.has("system0/PrimaryIpAddress")) {
        -							String ipv4 = JsonUtils.getAsPrimitive(jChannels, "system0/PrimaryIpAddress").getAsString();
        -							edge.setIpv4(ipv4);
        -						}
        -					} catch (OpenemsException e) {
        -						log.error("Edgde [" + edge.getName() + "] error: " + e.getMessage());
        +					// set specific Odoo values
        +					if (jChannels.has("ess0/Soc")) {
        +						int soc = JsonUtils.getAsPrimitive(jChannels, "ess0/Soc").getAsInt();
        +						edge.setSoc(soc);
        +					}
        +					if (jChannels.has("system0/PrimaryIpAddress")) {
        +						String ipv4 = JsonUtils.getAsPrimitive(jChannels, "system0/PrimaryIpAddress").getAsString();
        +						edge.setIpv4(ipv4);
         					}
        +				} catch (OpenemsException e) {
        +					log.error("Edgde [" + edge.getName() + "] error: " + e.getMessage());
         				}
         			}
         		}
        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 04356d47cd7..e707e1f8908 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
        @@ -23,6 +23,7 @@ public Edge(int id, String name, String comment, String producttype, JsonObject
         		this.name = name;
         		this.comment = comment;
         		this.producttype = producttype;
        +		this.jConfig = jConfig;
         	}
         
         	public int getId() {
        diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java
        index d2d464c040a..ca003e6615b 100644
        --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java
        +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/EdgeOnlineHandler.java
        @@ -1,7 +1,5 @@
         package io.openems.backend.metadata.api;
         
        -import java.util.Optional;
        -
         import org.osgi.service.component.annotations.Component;
         import org.osgi.service.component.annotations.Reference;
         import org.osgi.service.event.Event;
        @@ -11,6 +9,7 @@
         import org.slf4j.LoggerFactory;
         
         import io.openems.backend.common.events.BackendEventConstants;
        +import io.openems.common.exceptions.OpenemsException;
         
         @Component(property = { //
         		EventConstants.EVENT_TOPIC + "=" + BackendEventConstants.TOPIC_EDGE_ONLINE,
        @@ -25,21 +24,22 @@ public class EdgeOnlineHandler implements EventHandler {
         	@Override
         	public void handleEvent(Event event) {
         		int edgeId = (int) event.getProperty("edgeId");
        -		Optional edgeOpt = this.metadataService.getEdge(edgeId);
        -		if (edgeOpt.isPresent()) {
        -			Edge edge = edgeOpt.get();
        -			String topic = (String) event.getProperty("event.topics");
        -			if (topic.equals(BackendEventConstants.TOPIC_EDGE_ONLINE)) {
        -				edgeOpt.get().setOnline(true);
        -				log.debug("Marked Edge [" + edge.getName() + "] as Online");
        -			} else if (topic.equals(BackendEventConstants.TOPIC_EDGE_OFFLINE)) {
        -				edgeOpt.get().setOnline(false);
        -				log.debug("Marked Edge [" + edge.getName() + "] as Offline");
        -			} else {
        -				log.warn("Unknown Topic: " + topic);
        -			}
        +		Edge edge;
        +		try {
        +			edge = this.metadataService.getEdge(edgeId);
        +		} catch (OpenemsException e) {
        +			log.warn(e.getMessage());
        +			return;
        +		}
        +		String topic = (String) event.getProperty("event.topics");
        +		if (topic.equals(BackendEventConstants.TOPIC_EDGE_ONLINE)) {
        +			edge.setOnline(true);
        +			log.debug("Marked Edge [" + edge.getName() + "] as Online");
        +		} else if (topic.equals(BackendEventConstants.TOPIC_EDGE_OFFLINE)) {
        +			edge.setOnline(false);
        +			log.debug("Marked Edge [" + edge.getName() + "] as Offline");
         		} else {
        -			log.warn("Unable to get Edge [ID:" + edgeId + "]");
        +			log.warn("Unknown Topic: " + topic);
         		}
         	}
         }
        diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java
        index e2060ae9805..249045da72b 100644
        --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java
        +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/MetadataService.java
        @@ -10,10 +10,19 @@
         public interface MetadataService {
         
         	public abstract User getUserWithSession(String sessionId) throws OpenemsException;
        -	
        +
         	public abstract int[] getEdgeIdsForApikey(String apikey);
        -	
        -	public abstract Optional getEdge(int edgeId);
        -	
        +
        +	public abstract Optional getEdgeOpt(int edgeId);
        +
        +	public default Edge getEdge(int edgeId) throws OpenemsException {
        +		Optional edgeOpt = this.getEdgeOpt(edgeId);
        +		if (edgeOpt.isPresent()) {
        +			return edgeOpt.get();
        +		} else {
        +			throw new OpenemsException("Unable to get Edge for id [" + edgeId + "]");
        +		}
        +	}
        +
         	public abstract Optional getUser(int userId);
         }
        diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java
        index 1614458a531..482c0accf31 100644
        --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java
        +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java
        @@ -137,7 +137,7 @@ public User getUserWithSession(String sessionId) throws OpenemsException {
         					JsonArray jDevices = JsonUtils.getAsJsonArray(jResult, "devices");
         					for (JsonElement jDevice : jDevices) {
         						int edgeId = JsonUtils.getAsInt(jDevice, "id");
        -						Optional edgeOpt = this.getEdge(edgeId);
        +						Optional edgeOpt = this.getEdgeOpt(edgeId);
         						if (edgeOpt.isPresent()) {
         							Edge edge = edgeOpt.get();
         							synchronized (this.edges) {
        @@ -182,7 +182,7 @@ public int[] getEdgeIdsForApikey(String apikey) {
         	}
         
         	@Override
        -	public Optional getEdge(int edgeId) {
        +	public Optional getEdgeOpt(int edgeId) {
         		// try to read from cache
         		synchronized (this.edges) {
         			if (this.edges.containsKey(edgeId)) {
        diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java
        index 68a20920c72..317f12576f2 100644
        --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java
        +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java
        @@ -54,7 +54,7 @@ public interface TimedataService {
         	 * 	}]
         	 * 
        * - * @param deviceId + * @param edgeId * @param fromDate * @param toDate * @param channels @@ -62,6 +62,6 @@ public interface TimedataService { * @return * @throws OpenemsException */ - public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution/* , JsonObject kWh */) throws OpenemsException; } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index 4351fce0f41..924eef5ae12 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -91,7 +91,8 @@ void activate(Config config) throws OpenemsException { void deactivate() { log.debug("Deactivate InfluxDB"); if (this.influxDbOpt.isPresent()) { - // TODO this works only with a more recent version of Influxdb-Java this.influxDbOpt.get().close(); + // TODO this works only with a more recent version of Influxdb-Java + // this.influxDbOpt.get().close(); } } @@ -108,11 +109,11 @@ private synchronized InfluxDB getInfluxDbConnection() throws OpenemsException { return this.influxDbOpt.get(); } - private void writeData(int deviceId, TreeBasedTable data) throws OpenemsException { + private void writeData(int influxId, TreeBasedTable data) throws OpenemsException { InfluxDB influxDB = this.getInfluxDbConnection(); BatchPoints batchPoints = BatchPoints.database(database) // - .tag("fems", String.valueOf(deviceId)) // + .tag("fems", String.valueOf(influxId)) // .build(); for (Entry> entry : data.rowMap().entrySet()) { @@ -195,13 +196,13 @@ private void writeDataToOldMiniMonitoring(Edge edge, int influxId, TreeBasedTabl influxDB.write(batchPoints); } - - @Override - public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, - JsonObject channels, int resolution) throws OpenemsException { + public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + int resolution) throws OpenemsException { + Edge edge = this.metadataService.getEdge(edgeId); + int influxId = InfluxdbUtils.parseNumberFromName(edge.getName()); InfluxDB influxDB = getInfluxDbConnection(); - return InfluxdbUtils.queryHistoricData(influxDB, this.database, deviceIdOpt, fromDate, toDate, channels, + return InfluxdbUtils.queryHistoricData(influxDB, this.database, influxId, fromDate, toDate, channels, resolution); } @@ -223,106 +224,98 @@ public Optional getChannelValue(int edgeId, ChannelAddress channelAddres */ @Override public void write(int edgeId, JsonObject jData) throws OpenemsException { - Optional edgeOpt = this.metadataService.getEdge(edgeId); - if (edgeOpt.isPresent()) { - Edge edge = edgeOpt.get(); - Optional influxIdOpt = InfluxdbUtils.parseNumberFromName(edge.getName()); - if (influxIdOpt.isPresent()) { - int influxId = influxIdOpt.get(); - - TreeBasedTable data = TreeBasedTable.create(); - - // get existing or create new DeviceCache - DeviceCache deviceCache = this.deviceCacheMap.get(edgeId); - if (deviceCache == null) { - deviceCache = new DeviceCache(); - this.deviceCacheMap.put(edgeId, deviceCache); - } + Edge edge = this.metadataService.getEdge(edgeId); + int influxId = InfluxdbUtils.parseNumberFromName(edge.getName()); + TreeBasedTable data = TreeBasedTable.create(); - // Sort incoming data by timestamp - TreeMap sortedData = new TreeMap(); - for (Entry entry : jData.entrySet()) { - try { - Long timestamp = Long.valueOf(entry.getKey()); - JsonObject jChannels; - jChannels = JsonUtils.getAsJsonObject(entry.getValue()); - sortedData.put(timestamp, jChannels); - } catch (OpenemsException e) { - log.error("Data error: " + e.getMessage()); - } - } + // get existing or create new DeviceCache + DeviceCache deviceCache = this.deviceCacheMap.get(edgeId); + if (deviceCache == null) { + deviceCache = new DeviceCache(); + this.deviceCacheMap.put(edgeId, deviceCache); + } - // Prepare data table. Takes entries starting with eldest timestamp (ascending - // order) - for (Entry dataEntry : sortedData.entrySet()) { - Long timestamp = dataEntry.getKey(); - JsonObject jChannels = dataEntry.getValue(); + // Sort incoming data by timestamp + TreeMap sortedData = new TreeMap(); + for (Entry entry : jData.entrySet()) { + try { + Long timestamp = Long.valueOf(entry.getKey()); + JsonObject jChannels; + jChannels = JsonUtils.getAsJsonObject(entry.getValue()); + sortedData.put(timestamp, jChannels); + } catch (OpenemsException e) { + log.error("Data error: " + e.getMessage()); + } + } - if (jChannels.entrySet().size() == 0) { - // no channel values available. abort. - continue; - } + // Prepare data table. Takes entries starting with eldest timestamp (ascending + // order) + for (Entry dataEntry : sortedData.entrySet()) { + Long timestamp = dataEntry.getKey(); + JsonObject jChannels = dataEntry.getValue(); - // Check if cache is valid (it is not elder than 5 minutes compared to this - // timestamp) - long cacheTimestamp = deviceCache.getTimestamp(); - if (timestamp < cacheTimestamp) { - // incoming data is older than cache -> do not apply cache - } else { - // incoming data is more recent than cache - // update cache timestamp - deviceCache.setTimestamp(timestamp); - - if (timestamp < cacheTimestamp + 5 * 60 * 1000) { - // cache is valid (not elder than 5 minutes) - // add cache data to write data - for (Entry cacheEntry : deviceCache.getChannelCacheEntries()) { - String channel = cacheEntry.getKey(); - Object value = cacheEntry.getValue(); - data.put(timestamp, channel, value); - } - } else { - // cache is not anymore valid (elder than 5 minutes) - // clear cache - if (cacheTimestamp != 0l) { - log.info("Edge [" + edge.getName() + "]: invalidate cache for influxId [" + influxId - + "]. This timestamp [" + timestamp + "]. Cache timestamp [" + cacheTimestamp - + "]"); - } - deviceCache.clear(); - } - - // add incoming data to cache (this replaces already existing cache values) - for (Entry channelEntry : jChannels.entrySet()) { - String channel = channelEntry.getKey(); - Optional valueOpt = InfluxdbUtils.parseValue(channel, channelEntry.getValue()); - if (valueOpt.isPresent()) { - Object value = valueOpt.get(); - deviceCache.putToChannelCache(channel, value); - } - } - } + if (jChannels.entrySet().size() == 0) { + // no channel values available. abort. + continue; + } - // add incoming data to write data - for (Entry channelEntry : jChannels.entrySet()) { - String channel = channelEntry.getKey(); - Optional valueOpt = InfluxdbUtils.parseValue(channel, channelEntry.getValue()); - if (valueOpt.isPresent()) { - Object value = valueOpt.get(); - data.put(timestamp, channel, value); - } + // Check if cache is valid (it is not elder than 5 minutes compared to this + // timestamp) + long cacheTimestamp = deviceCache.getTimestamp(); + if (timestamp < cacheTimestamp) { + // incoming data is older than cache -> do not apply cache + } else { + // incoming data is more recent than cache + // update cache timestamp + deviceCache.setTimestamp(timestamp); + + if (timestamp < cacheTimestamp + 5 * 60 * 1000) { + // cache is valid (not elder than 5 minutes) + // add cache data to write data + for (Entry cacheEntry : deviceCache.getChannelCacheEntries()) { + String channel = cacheEntry.getKey(); + Object value = cacheEntry.getValue(); + data.put(timestamp, channel, value); } + } else { + // cache is not anymore valid (elder than 5 minutes) + // clear cache + if (cacheTimestamp != 0l) { + log.info("Edge [" + edge.getName() + "]: invalidate cache for influxId [" + influxId + + "]. This timestamp [" + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); + } + deviceCache.clear(); } - // Write data to default location - writeData(influxId, data); + // add incoming data to cache (this replaces already existing cache values) + for (Entry channelEntry : jChannels.entrySet()) { + String channel = channelEntry.getKey(); + Optional valueOpt = InfluxdbUtils.parseValue(channel, channelEntry.getValue()); + if (valueOpt.isPresent()) { + Object value = valueOpt.get(); + deviceCache.putToChannelCache(channel, value); + } + } + } - // Hook to continue writing data to old Mini monitoring - // TODO remove after full migration - if (edge.getProducttype().equals("MiniES 3-3")) { - writeDataToOldMiniMonitoring(edge, influxId, data); + // add incoming data to write data + for (Entry channelEntry : jChannels.entrySet()) { + String channel = channelEntry.getKey(); + Optional valueOpt = InfluxdbUtils.parseValue(channel, channelEntry.getValue()); + if (valueOpt.isPresent()) { + Object value = valueOpt.get(); + data.put(timestamp, channel, value); } } } + + // Write data to default location + writeData(influxId, data); + + // Hook to continue writing data to old Mini monitoring + // TODO remove after full migration + if (edge.getProducttype().equals("MiniES 3-3")) { + writeDataToOldMiniMonitoring(edge, influxId, data); + } } } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java index 52c310bcb6b..2545b28078a 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java @@ -34,18 +34,14 @@ public class InfluxdbUtils { private final static Logger log = LoggerFactory.getLogger(InfluxdbUtils.class); - - public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional deviceId, - ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { + + public static JsonArray queryHistoricData(InfluxDB influxdb, String database, int influxId, ZonedDateTime fromDate, + ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { // Prepare query string StringBuilder query = new StringBuilder("SELECT "); query.append(toChannelAddressList(channels)); query.append(" FROM data WHERE "); - if (deviceId.isPresent()) { - query.append("fems = '"); - query.append(deviceId.get()); - query.append("' AND "); - } + query.append("fems = '" + influxId + "' AND "); query.append("time > "); query.append(String.valueOf(fromDate.toEpochSecond())); query.append("s"); @@ -381,12 +377,12 @@ private static QueryResult executeQuery(InfluxDB influxdb, String database, Stri private final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); - public static Optional parseNumberFromName(String name) { + public static Integer parseNumberFromName(String name) throws OpenemsException { Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); if (matcher.find()) { String nameNumberString = matcher.group(1); - return Optional.ofNullable(Integer.parseInt(nameNumberString)); + return Integer.parseInt(nameNumberString); } - return Optional.empty(); + throw new OpenemsException("Unable to parse number from name [" + name + "]"); } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index a751f40acf3..888bfc038b9 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -1,6 +1,10 @@ package io.openems.backend.uiwebsocket.impl.provider; import java.util.Map.Entry; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -58,7 +62,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { websocket.closeConnection(CloseFrame.REFUSE, error); return; } - + UUID uuid = UUID.randomUUID(); synchronized (this.websocketsMap) { // add websocket to local cache @@ -72,13 +76,15 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { for (Entry edgeRole : user.getEdgeRoles().entrySet()) { int edgeId = edgeRole.getKey(); Role role = edgeRole.getValue(); - Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); - if (!edgeOpt.isPresent()) { - log.warn("Unable to find Edge [ID:" + edgeId + "]"); - } else { - JsonObject jEdge = edgeOpt.get().toJsonObject(); + Edge edge; + try { + edge = this.parent.metadataService.getEdge(edgeId); + JsonObject jEdge = edge.toJsonObject(); jEdge.addProperty("role", role.toString()); jEdges.add(jEdge); + } catch (OpenemsException e) { + // TODO handle error + log.warn(e.getMessage()); } } JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply("" /* TODO empty token? */, @@ -144,34 +150,27 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { } // get Edge - Optional edgeOpt = this.parent.metadataService.getEdge(edgeId); - if (!edgeOpt.isPresent()) { - // TODO Error unable to find Edge + Edge edge; + try { + edge = this.parent.metadataService.getEdge(edgeId); + } catch (OpenemsException e) { + // TODO handle error + log.error(e.getMessage()); return; } - Edge edge = edgeOpt.get(); /* - * TODO Query historic data + * Query historic data */ - // if (jMessage.has("historicData")) { - // // parse deviceId - // JsonArray jMessageId = jMessageIdOpt.get(); - // try { - // JsonObject jHistoricData = JsonUtils.getAsJsonObject(jMessage, - // "historicData"); - // JsonObject jReply = WebSocketUtils.historicData(jMessageId, jHistoricData, - // deviceIdOpt, - // Timedata.instance(), Role.ADMIN); - // // TODO read role from device - // WebSocketUtils.send(websocket, jReply); - // } catch (OpenemsException e) { - // log.error(e.getMessage()); - // } - // } + Optional jHistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); + if (jHistoricDataOpt.isPresent()) { + JsonObject jHistoricData = jHistoricDataOpt.get(); + JsonObject jReply = this.historicData(messageId, edgeId, jHistoricData); + WebSocketUtils.send(websocket, jReply); + } /* - * TODO Subscribe to currentData + * Subscribe to currentData */ Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); if (jCurrentDataOpt.isPresent()) { @@ -260,4 +259,46 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, S } } + /** + * Query history command + * + * @param j + */ + private JsonObject historicData(String jMessageId, int edgeId, JsonObject jHistoricData) { + try { + String mode = JsonUtils.getAsString(jHistoricData, "mode"); + + if (mode.equals("query")) { + /* + * Query historic data + */ + int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); + ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); + ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); + ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); + JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); + // TODO check if role is allowed to read these channels + // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); + int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); + // TODO: better calculation of sensible resolution + int resolution = 10 * 60; // 10 Minutes + if (days > 25) { + resolution = 24 * 60 * 60; // 1 Day + } else if (days > 6) { + resolution = 3 * 60 * 60; // 3 Hours + } else if (days > 2) { + resolution = 60 * 60; // 60 Minutes + } + JsonArray jData = this.parent.timeDataService.queryHistoricData(edgeId, fromDate, toDate, channels, + resolution); + // send reply + return DefaultMessages.historicDataQueryReply(jMessageId, jData); + } + } catch (Exception e) { + // TODO handle exception + log.warn(e.getMessage()); + } + return new JsonObject(); + } + } diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 02952bc1fee..e512639e1bf 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -195,7 +195,7 @@ public static JsonObject currentData(String messageId, JsonObject jCurrentData) /** *
         	 *	{
        -	 *		id: [string]
        +	 *		messageId: UUID,
         	 *		historicData: {
         	 *			data: [{
         	 *				time: ...,
        @@ -211,9 +211,8 @@ public static JsonObject currentData(String messageId, JsonObject jCurrentData)
         	 * 
         	 * @return
         	 */
        -	public static JsonObject historicDataQueryReply(JsonArray jId, JsonArray jData) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        +	public static JsonObject historicDataQueryReply(String messageId, JsonArray jData) {
        +		JsonObject j = newMessage(messageId);
         		JsonObject jHistoricData = new JsonObject();
         		jHistoricData.add("data", jData);
         		j.add("historicData", jHistoricData);
        diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts
        index 70d18543304..4c5377ea08e 100644
        --- a/ui/src/app/shared/device/device.ts
        +++ b/ui/src/app/shared/device/device.ts
        @@ -26,7 +26,7 @@ export class Log {
         export class Device {
         
           constructor(
        -    public readonly id: number,
        +    public readonly edgeId: number,
             public readonly name: string,
             public readonly comment: string,
             public readonly producttype: string,
        @@ -70,7 +70,7 @@ export class Device {
            * Refresh the config
            */
           public refreshConfig(): BehaviorSubject {
        -    let message = DefaultMessages.configQuery(this.id);
        +    let message = DefaultMessages.configQuery(this.edgeId);
             let messageId = message.messageId;
             this.replyStreams[messageId] = new Subject();
             this.send(message);
        @@ -93,19 +93,25 @@ export class Device {
             this.websocket.send(value);
           }
         
        -  private sendMessageWithReply(message: DefaultTypes.OutgoingMessage): Subject {
        +  private sendMessageWithReply(message: DefaultTypes.IdentifiedMessage): Subject {
             let messageId: string = message.messageId;
             this.replyStreams[messageId] = new Subject();
             this.send(message);
             return this.replyStreams[messageId];
           }
         
        +  private removeReplyStream(reply: DefaultMessages.Reply) {
        +    let messageId: string = reply.messageId;
        +    this.replyStreams[messageId].unsubscribe();
        +    delete this.replyStreams[messageId];
        +  }
        +
           /**
            * Subscribe to current data of specified channels
            */
           public subscribeCurrentData(channels: DefaultTypes.ChannelAddresses): Observable {
             this.subscribeCurrentDataChannels = channels;
        -    let replyStream = this.sendMessageWithReply(DefaultMessages.currentDataSubscribe(this.id, channels));
        +    let replyStream = this.sendMessageWithReply(DefaultMessages.currentDataSubscribe(this.edgeId, channels));
             let obs = replyStream
               .map(message => (message as DefaultMessages.CurrentDataReply).currentData)
               .combineLatest(this.config, (currentData, config) => new CurrentDataAndSummary(currentData, config));
        @@ -125,18 +131,13 @@ export class Device {
            */
           // TODO: kWh: this.getkWhResult(this.getImportantChannels())
           public historicDataQuery(fromDate: Date, toDate: Date, channels: DefaultTypes.ChannelAddresses): Promise {
        -    // send query
             let timezone = new Date().getTimezoneOffset() * 60;
        -    let message = DefaultMessages.historicDataQuery(fromDate, toDate, timezone, channels);
        -    let messageId = message.id[0];
        -    this.replyStreams[messageId] = new Subject();
        -    this.send(message);
        +    let replyStream = this.sendMessageWithReply(DefaultMessages.historicDataQuery(this.edgeId, fromDate, toDate, timezone, channels));
             // wait for reply
             return new Promise((resolve, reject) => {
        -      this.replyStreams[messageId].first().subscribe(reply => {
        +      replyStream.first().subscribe(reply => {
                 let historicData = (reply).historicData;
        -        this.replyStreams[messageId].unsubscribe();
        -        delete this.replyStreams[messageId];
        +        this.removeReplyStream(reply);
                 resolve(historicData);
               });
             })
        diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts
        index d403f753944..e20333164b4 100644
        --- a/ui/src/app/shared/service/defaultmessages.ts
        +++ b/ui/src/app/shared/service/defaultmessages.ts
        @@ -58,10 +58,10 @@ export class DefaultMessages {
                 }
             };
         
        -    public static historicDataQuery(fromDate: Date, toDate: Date, timezone: number /*offset in seconds*/, channels: DefaultTypes.ChannelAddresses) {
        +    public static historicDataQuery(edgeId: number, fromDate: Date, toDate: Date, timezone: number /*offset in seconds*/, channels: DefaultTypes.ChannelAddresses) {
                 return {
        -            device: String,
        -            id: [UUID.UUID()],
        +            messageId: UUID.UUID(),
        +            edgeId: edgeId,
                     historicData: {
                         mode: "query",
                         fromDate: format(fromDate, 'YYYY-MM-DD'),
        @@ -112,8 +112,8 @@ export class DefaultMessages {
         }
         
         export module DefaultMessages {
        -    export interface Reply {
        -        id: string[]
        +
        +    export interface Reply extends DefaultTypes.IdentifiedMessage {
             }
         
             export interface ConfigQueryReply extends Reply {
        diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts
        index 3623df124c2..2c307f6f851 100644
        --- a/ui/src/app/shared/service/defaulttypes.ts
        +++ b/ui/src/app/shared/service/defaulttypes.ts
        @@ -109,11 +109,11 @@ export module DefaultTypes {
         
           export type LanguageTag = "de" | "en" | "cz" | "nl";
         
        -  export interface OutgoingMessage {
        +  export interface IdentifiedMessage {
             messageId: string
           }
         
        -  export interface ConfigUpdate extends OutgoingMessage {
        +  export interface ConfigUpdate extends IdentifiedMessage {
             config: {
               mode: "update",
               thing: string,
        
        From 2611b1f2ff6637f8bd4d08a9c30dec28a74cabcf Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Fri, 23 Feb 2018 18:23:19 +0100
        Subject: [PATCH 096/156] Recover deleted files from "common"
        
        ---
         .../io/openems/common/api/TimedataSource.java |  36 ++
         .../exceptions/NotImplementedException.java   |  30 ++
         .../common/exceptions/OpenemsException.java   |  13 +
         .../src/io/openems/common/session/Role.java   |  23 ++
         .../io/openems/common/session/Session.java    |  28 ++
         .../openems/common/session/SessionData.java   |   7 +
         .../common/session/SessionManager.java        | 126 +++++++
         .../openems/common/types/ChannelAddress.java  |  43 +++
         .../src/io/openems/common/types/Device.java   |  21 ++
         .../io/openems/common/types/DeviceImpl.java   |  61 +++
         .../io/openems/common/types/FieldValue.java   |  29 ++
         .../openems/common/types/NullFieldValue.java  |  29 ++
         .../common/types/NumberFieldValue.java        |  29 ++
         .../common/types/StringFieldValue.java        |  29 ++
         .../common/types/TimestampedFieldValue.java   |  19 +
         .../openems/common/utils/InfluxdbUtils.java   | 311 +++++++++++++++
         .../io/openems/common/utils/JsonUtils.java    | 342 +++++++++++++++++
         .../common/utils/SecureRandomSingleton.java   |  36 ++
         .../io/openems/common/utils/StringUtils.java  |  19 +
         .../websocket/AbstractWebsocketServer.java    |   1 -
         .../common/websocket/CurrentDataWorker.java   |  81 ++++
         .../common/websocket/DefaultMessages.java     | 357 ++++++++++++++++++
         .../common/websocket/LogBehaviour.java        |   5 +
         .../common/websocket/Notification.java        |  54 +++
         .../common/websocket/NotificationType.java    |  25 ++
         .../common/websocket/WebSocketUtils.java      | 113 ++++++
         .../io/openems/common/utils/JsonUtils.java    |   6 -
         27 files changed, 1866 insertions(+), 7 deletions(-)
         create mode 100644 common/src/io/openems/common/api/TimedataSource.java
         create mode 100644 common/src/io/openems/common/exceptions/NotImplementedException.java
         create mode 100644 common/src/io/openems/common/exceptions/OpenemsException.java
         create mode 100644 common/src/io/openems/common/session/Role.java
         create mode 100644 common/src/io/openems/common/session/Session.java
         create mode 100644 common/src/io/openems/common/session/SessionData.java
         create mode 100644 common/src/io/openems/common/session/SessionManager.java
         create mode 100644 common/src/io/openems/common/types/ChannelAddress.java
         create mode 100644 common/src/io/openems/common/types/Device.java
         create mode 100644 common/src/io/openems/common/types/DeviceImpl.java
         create mode 100644 common/src/io/openems/common/types/FieldValue.java
         create mode 100644 common/src/io/openems/common/types/NullFieldValue.java
         create mode 100644 common/src/io/openems/common/types/NumberFieldValue.java
         create mode 100644 common/src/io/openems/common/types/StringFieldValue.java
         create mode 100644 common/src/io/openems/common/types/TimestampedFieldValue.java
         create mode 100644 common/src/io/openems/common/utils/InfluxdbUtils.java
         create mode 100644 common/src/io/openems/common/utils/JsonUtils.java
         create mode 100644 common/src/io/openems/common/utils/SecureRandomSingleton.java
         create mode 100644 common/src/io/openems/common/utils/StringUtils.java
         create mode 100644 common/src/io/openems/common/websocket/CurrentDataWorker.java
         create mode 100644 common/src/io/openems/common/websocket/DefaultMessages.java
         create mode 100644 common/src/io/openems/common/websocket/LogBehaviour.java
         create mode 100644 common/src/io/openems/common/websocket/Notification.java
         create mode 100644 common/src/io/openems/common/websocket/NotificationType.java
         create mode 100644 common/src/io/openems/common/websocket/WebSocketUtils.java
        
        diff --git a/common/src/io/openems/common/api/TimedataSource.java b/common/src/io/openems/common/api/TimedataSource.java
        new file mode 100644
        index 00000000000..0744d3a81d4
        --- /dev/null
        +++ b/common/src/io/openems/common/api/TimedataSource.java
        @@ -0,0 +1,36 @@
        +package io.openems.common.api;
        +
        +import java.time.ZonedDateTime;
        +import java.util.Optional;
        +
        +import com.google.gson.JsonArray;
        +import com.google.gson.JsonObject;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +
        +public interface TimedataSource {
        +	/**
        +	 * Queries the database and returns a JsonArray of the form
        +	 *
        +	 * 
        +	 *	[{
        +	 *  	timestamp: "2017-03-21T08:55:20Z",
        +	 *  	channels: {
        +	 *			'thing': {
        +	 *				'channel': 'value'
        +	 *			}
        +	 *		}
        +	 * 	}]
        +	 * 
        + * + * @param deviceId + * @param fromDate + * @param toDate + * @param channels + * @param resolution + * @return + * @throws OpenemsException + */ + public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + int resolution/* , JsonObject kWh */) throws OpenemsException; +} diff --git a/common/src/io/openems/common/exceptions/NotImplementedException.java b/common/src/io/openems/common/exceptions/NotImplementedException.java new file mode 100644 index 00000000000..1990b3690fc --- /dev/null +++ b/common/src/io/openems/common/exceptions/NotImplementedException.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.exceptions; + +public class NotImplementedException extends OpenemsException { + + private static final long serialVersionUID = 1L; + + public NotImplementedException(String message) { + super(message); + } +} diff --git a/common/src/io/openems/common/exceptions/OpenemsException.java b/common/src/io/openems/common/exceptions/OpenemsException.java new file mode 100644 index 00000000000..bea21e0044b --- /dev/null +++ b/common/src/io/openems/common/exceptions/OpenemsException.java @@ -0,0 +1,13 @@ +package io.openems.common.exceptions; + +public class OpenemsException extends Exception { + private static final long serialVersionUID = 5015013132334439401L; + + public OpenemsException(String message) { + super(message); + } + + public OpenemsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/src/io/openems/common/session/Role.java b/common/src/io/openems/common/session/Role.java new file mode 100644 index 00000000000..3d6c44e55ca --- /dev/null +++ b/common/src/io/openems/common/session/Role.java @@ -0,0 +1,23 @@ +package io.openems.common.session; + +public enum Role { + ADMIN, INSTALLER, OWNER, GUEST; + + public static Role getRole(String name) { + switch (name.toLowerCase()) { + case "admin": + return ADMIN; + case "installer": + return INSTALLER; + case "owner": + return OWNER; + case "guest": + default: + return GUEST; + } + } + + public static Role getDefaultRole() { + return GUEST; + } +} diff --git a/common/src/io/openems/common/session/Session.java b/common/src/io/openems/common/session/Session.java new file mode 100644 index 00000000000..829266059d7 --- /dev/null +++ b/common/src/io/openems/common/session/Session.java @@ -0,0 +1,28 @@ +package io.openems.common.session; + +public class Session { + private final String token; + + /** + * store additional metadata to this session + */ + private final D data; + + protected Session(String token, D data) { + this.token = token; + this.data = data; + } + + public String getToken() { + return token; + } + + public D getData() { + return data; + } + + @Override + public String toString() { + return "Session [token=" + token + ", data=" + data + "]"; + } +} diff --git a/common/src/io/openems/common/session/SessionData.java b/common/src/io/openems/common/session/SessionData.java new file mode 100644 index 00000000000..bd2b6b1aff6 --- /dev/null +++ b/common/src/io/openems/common/session/SessionData.java @@ -0,0 +1,7 @@ +package io.openems.common.session; + +import com.google.gson.JsonObject; + +public abstract class SessionData { + public abstract JsonObject toJsonObject(); +} diff --git a/common/src/io/openems/common/session/SessionManager.java b/common/src/io/openems/common/session/SessionManager.java new file mode 100644 index 00000000000..2452d5f5224 --- /dev/null +++ b/common/src/io/openems/common/session/SessionManager.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.session; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.utils.SecureRandomSingleton; + +public abstract class SessionManager, D extends SessionData> { + + private final Logger log = LoggerFactory.getLogger(SessionManager.class); + private final static int SESSION_ID_LENGTH = 130; + + // TODO: invalidate old sessions in separate thread: call _removeSession to do + // so + private final Map sessions = new ConcurrentHashMap<>(); + + protected SessionManager() { + } + + public S createNewSession(String token, D data) { + S session = this._createNewSession(token, data); + this._putSession(token, session); + return session; + } + + public S createNewSession(D data) { + String token = this.generateToken(); + return this.createNewSession(token, data); + } + + public Optional getSessionByToken(String token) { + synchronized (this.sessions) { + return Optional.ofNullable(this.sessions.get(token)); + } + } + + public void removeSession(String token) { + synchronized (this.sessions) { + S session = this.sessions.get(token); + if (session != null) { + this._removeSession(token); + } + } + } + + public void removeSession(Session session) { + this.removeSession(session.getToken()); + } + + protected String generateToken() { + // Source: http://stackoverflow.com/a/41156 + SecureRandom sr = SecureRandomSingleton.getInstance(); + return new BigInteger(SESSION_ID_LENGTH, sr).toString(32); + } + + public Collection getSessions() { + return Collections.unmodifiableCollection(this.sessions.values()); + } + + /* + * Those methods are prone to be overwritten by inheritance + */ + /** + * Replies a Session object of type T + * + * @param token + * @param websocket + * @param data + * @return + */ + protected abstract S _createNewSession(String token, D data); + + /** + * This method is always called when adding a session to local database + * + * @param token + * @param session + */ + protected void _putSession(String token, S session) { + synchronized (this.sessions) { + if (this.sessions.containsKey(token)) { + log.warn("Session with token [" + token + "] already existed. Replacing with session [" + session + "]"); + } + this.sessions.put(token, session); + } + } + + /** + * This method is always called when removing a session from local database + * + * @param session + */ + protected void _removeSession(String token) { + synchronized (this.sessions) { + this.sessions.remove(token); + } + } +} diff --git a/common/src/io/openems/common/types/ChannelAddress.java b/common/src/io/openems/common/types/ChannelAddress.java new file mode 100644 index 00000000000..a5f805e9ef0 --- /dev/null +++ b/common/src/io/openems/common/types/ChannelAddress.java @@ -0,0 +1,43 @@ +package io.openems.common.types; + +import io.openems.common.exceptions.OpenemsException; + +public class ChannelAddress implements Comparable { + private final String thingId; + private final String channelId; + + public ChannelAddress(String thingId, String channelId) { + super(); + this.thingId = thingId; + this.channelId = channelId; + } + + public String getThingId() { + return thingId; + } + + public String getChannelId() { + return channelId; + } + + @Override + public String toString() { + return thingId + "/" + channelId; + } + + public static ChannelAddress fromString(String address) throws OpenemsException { + try { + String[] addressArray = address.split("/"); + String thingId = addressArray[0]; + String channelId = addressArray[1]; + return new ChannelAddress(thingId, channelId); + } catch (Exception e) { + throw new OpenemsException("This [" + address + "] is not a valid channel address."); + } + } + + @Override + public int compareTo(ChannelAddress other) { + return this.toString().compareTo(other.toString()); + } +} diff --git a/common/src/io/openems/common/types/Device.java b/common/src/io/openems/common/types/Device.java new file mode 100644 index 00000000000..6bf2309f41b --- /dev/null +++ b/common/src/io/openems/common/types/Device.java @@ -0,0 +1,21 @@ +package io.openems.common.types; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public interface Device { + + final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); + + public Optional getIdOpt(); + + public static Optional parseNumberFromName(String name) { + Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); + if (matcher.find()) { + String nameNumberString = matcher.group(1); + return Optional.ofNullable(Integer.parseInt(nameNumberString)); + } + return Optional.empty(); + } +} diff --git a/common/src/io/openems/common/types/DeviceImpl.java b/common/src/io/openems/common/types/DeviceImpl.java new file mode 100644 index 00000000000..124d8e721a9 --- /dev/null +++ b/common/src/io/openems/common/types/DeviceImpl.java @@ -0,0 +1,61 @@ +package io.openems.common.types; + +import java.util.Optional; + +import io.openems.common.session.Role; + +/** + * Helper class to store tuple of device name and role + * + * @author stefan.feilmeier + * + */ +public class DeviceImpl implements Comparable, Device { + private final String name; + private final String comment; + private final String producttype; + private final Role role; + private boolean online = false; + + public DeviceImpl(String name, String comment, String producttype, String role) { + this.name = name; + this.comment = comment; + this.producttype = producttype; + this.role = Role.getRole(role); + } + + public String getName() { + return name; + } + + public Role getRole() { + return role; + } + + public void setOnline(boolean online) { + this.online = online; + } + + public boolean isOnline() { + return online; + } + + public String getComment() { + return comment; + } + + public String getProducttype() { + return producttype; + } + + @Override + public int compareTo(DeviceImpl other) { + return (this.name + this.comment + this.producttype + this.role.toString() + this.online) + .compareTo(other.name + other.comment + other.producttype + other.role.toString() + other.online); + } + + @Override + public Optional getIdOpt() { + return Device.parseNumberFromName(this.getName()); + } +} diff --git a/common/src/io/openems/common/types/FieldValue.java b/common/src/io/openems/common/types/FieldValue.java new file mode 100644 index 00000000000..aac1c08db2e --- /dev/null +++ b/common/src/io/openems/common/types/FieldValue.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public abstract class FieldValue { + public final T value; + + public FieldValue(T value) { + this.value = value; + } +} diff --git a/common/src/io/openems/common/types/NullFieldValue.java b/common/src/io/openems/common/types/NullFieldValue.java new file mode 100644 index 00000000000..3e889aa8636 --- /dev/null +++ b/common/src/io/openems/common/types/NullFieldValue.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public class NullFieldValue extends FieldValue { + + public NullFieldValue() { + super(null); + } + +} diff --git a/common/src/io/openems/common/types/NumberFieldValue.java b/common/src/io/openems/common/types/NumberFieldValue.java new file mode 100644 index 00000000000..355804fc9a9 --- /dev/null +++ b/common/src/io/openems/common/types/NumberFieldValue.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public class NumberFieldValue extends FieldValue { + + public NumberFieldValue(Number value) { + super(value); + } + +} diff --git a/common/src/io/openems/common/types/StringFieldValue.java b/common/src/io/openems/common/types/StringFieldValue.java new file mode 100644 index 00000000000..9ed25e24d93 --- /dev/null +++ b/common/src/io/openems/common/types/StringFieldValue.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.types; + +public class StringFieldValue extends FieldValue { + + public StringFieldValue(String value) { + super(value); + } + +} diff --git a/common/src/io/openems/common/types/TimestampedFieldValue.java b/common/src/io/openems/common/types/TimestampedFieldValue.java new file mode 100644 index 00000000000..a2837de8149 --- /dev/null +++ b/common/src/io/openems/common/types/TimestampedFieldValue.java @@ -0,0 +1,19 @@ +package io.openems.common.types; + +public class TimestampedFieldValue { + private long timestamp; + private FieldValue value; + + public TimestampedFieldValue(long timestamp, FieldValue value) { + this.timestamp = timestamp; + this.value = value; + } + + public long getTimestamp() { + return timestamp; + } + + public FieldValue getValue() { + return value; + } +} diff --git a/common/src/io/openems/common/utils/InfluxdbUtils.java b/common/src/io/openems/common/utils/InfluxdbUtils.java new file mode 100644 index 00000000000..656ac49319f --- /dev/null +++ b/common/src/io/openems/common/utils/InfluxdbUtils.java @@ -0,0 +1,311 @@ +package io.openems.common.utils; + +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.influxdb.InfluxDB; +import org.influxdb.dto.Query; +import org.influxdb.dto.QueryResult; +import org.influxdb.dto.QueryResult.Result; +import org.influxdb.dto.QueryResult.Series; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; + +public class InfluxdbUtils { + + public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional deviceId, + ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { + // Prepare query string + StringBuilder query = new StringBuilder("SELECT "); + query.append(toChannelAddressList(channels)); + query.append(" FROM data WHERE "); + if (deviceId.isPresent()) { + query.append("fems = '"); + query.append(deviceId.get()); + query.append("' AND "); + } + query.append("time > "); + query.append(String.valueOf(fromDate.toEpochSecond())); + query.append("s"); + query.append(" AND time < "); + query.append(String.valueOf(toDate.toEpochSecond())); + query.append("s"); + query.append(" GROUP BY time("); + query.append(resolution); + query.append("s) fill(null)"); + + QueryResult queryResult = executeQuery(influxdb, database, query.toString()); + + JsonArray j = new JsonArray(); + for (Result result : queryResult.getResults()) { + List seriess = result.getSeries(); + if (seriess != null) { + for (Series series : seriess) { + // create thing/channel index + ArrayList addressIndex = new ArrayList<>(); + for (String column : series.getColumns()) { + if (column.equals("time")) { + continue; + } + addressIndex.add(ChannelAddress.fromString(column)); + } + // first: create empty timestamp objects + for (List values : series.getValues()) { + JsonObject jTimestamp = new JsonObject(); + // get timestamp + Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) values.get(0)).doubleValue()); + ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); + String timestampString = timestamp.format(DateTimeFormatter.ISO_INSTANT); + jTimestamp.addProperty("time", timestampString); + // add empty channels by copying "channels" parameter + JsonObject jChannels = new JsonObject(); + for (Entry entry : channels.entrySet()) { + String thingId = entry.getKey(); + JsonObject jThing = new JsonObject(); + JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement channelElement : channelIds) { + String channelId = JsonUtils.getAsString(channelElement); + jThing.add(channelId, JsonNull.INSTANCE); + } + jChannels.add(thingId, jThing); + } + jTimestamp.add("channels", jChannels); + j.add(jTimestamp); + } + // then: add all data + for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { + for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { + Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); + ChannelAddress address = addressIndex.get(columnIndex - 1); + j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() + .get(address.getThingId()).getAsJsonObject() + .addProperty(address.getChannelId(), value); + } + } + } + } + } + return j; + } + +// private static JsonObject querykWh(InfluxDB influxdb, String database, Optional fems, +// ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh) +// throws OpenemsException { +// JsonArray gridThing = getGridThing(kWh); +// JsonArray storageThing = getStorageThing(kWh); +// JsonArray things = new JsonArray(); +// things.addAll(storageThing); +// things.addAll(gridThing); +// +// JsonObject jThing = new JsonObject(); +// ArrayList productionChannels = toChannelAddressListAvg(channels, things); +// +// for (int i = 0; i < productionChannels.size(); i++) { +// /* +// * SUM data +// */ +// StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); +// query.append(productionChannels.get(i)); +// query.append("\") AS AP FROM data WHERE "); +// if (fems.isPresent()) { +// query.append("fems = '"); +// query.append(fems.get()); +// query.append("' AND "); +// } +// query.append("time > "); +// query.append(String.valueOf(fromDate.toEpochSecond())); +// query.append("s"); +// query.append(" AND time < "); +// query.append(String.valueOf(toDate.toEpochSecond())); +// query.append("s"); +// query.append(" GROUP BY time(1s) fill(previous))"); +// +// QueryResult queryResult = executeQuery(influxdb, database, query.toString()); +// +// Double sumProduction = 0.0; +// try { +// for (Result result : queryResult.getResults()) { +// for (Series serie : result.getSeries()) { +// for (List l : serie.getValues()) { +// sumProduction = (Double) l.get(1); +// } +// } +// } +// } catch (Exception e) { +// System.out.println("Error parsing SUM production: " + e); +// } +// +// /* +// * FIRST production data +// */ +// query = new StringBuilder("SELECT FIRST(\""); +// query.append(productionChannels.get(i)); +// query.append("\") FROM data WHERE "); +// if (fems.isPresent()) { +// query.append("fems = '"); +// query.append(fems.get()); +// query.append("' AND "); +// } +// query.append("time > "); +// query.append(String.valueOf(fromDate.toEpochSecond())); +// query.append("s"); +// query.append(" AND time < "); +// query.append(String.valueOf(toDate.toEpochSecond())); +// query.append("s"); +// +// queryResult = executeQuery(influxdb, database, query.toString()); +// +// int second = 0; +// try { +// for (Result result : queryResult.getResults()) { +// for (Series serie : result.getSeries()) { +// for (List l : serie.getValues()) { +// Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); +// ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); +// if (timestamp.equals(fromDate)) { +// System.out.println("Parsing FIRST: nothing null"); +// } else { +// second = timestamp.getSecond(); +// } +// } +// } +// } +// } catch (Exception e) { +// System.out.println("Error parsing FIRST production: " + e); +// } +// +// /* +// * LAST data +// */ +// query = new StringBuilder("SELECT LAST(\""); +// query.append(productionChannels.get(i)); +// query.append("\") FROM data WHERE "); +// if (fems.isPresent()) { +// query.append("fems = '"); +// query.append(fems.get()); +// query.append("' AND "); +// } +// query.append("time < "); +// query.append(String.valueOf(fromDate.toEpochSecond())); +// query.append("s"); +// +// queryResult = executeQuery(influxdb, query.toString(), database); +// +// try { +// if (queryResult.getResults() != null) { +// for (Result result : queryResult.getResults()) { +// if (result.getSeries() != null) { +// for (Series serie : result.getSeries()) { +// if (serie.getValues() != null) { +// for (List l : serie.getValues()) { +// if (l.get(1) != null) { +// sumProduction += (Double) l.get(1) * second; +// } +// } +// } +// } +// } +// } +// } +// } catch (Exception e) { +// System.out.println("Error parsing LAST production: " + e); +// } +// +// Double avg = sumProduction / 3600 / 1000; +// +// JsonObject element = new JsonObject(); +// element.addProperty("value", avg); +// element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); +// jThing.add(productionChannels.get(i).toString(), element); +// } +// +// return jThing; +// } + +// private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { +// JsonArray gridThing = new JsonArray(); +// for (Entry entry : kWh.entrySet()) { +// String thingId = entry.getKey(); +// if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { +// gridThing.add(thingId); +// } +// } +// return gridThing; +// } +// +// private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { +// JsonArray storageThing = new JsonArray(); +// for (Entry entry : kWh.entrySet()) { +// String thingId = entry.getKey(); +// if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { +// storageThing.add(thingId); +// } +// } +// return storageThing; +// } +// +// private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) +// throws OpenemsException { +// ArrayList channelAddresses = new ArrayList<>(); +// for (Entry entry : channels.entrySet()) { +// String thingId = entry.getKey(); +// JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); +// for (JsonElement channelElement : channelIds) { +// String channelId = JsonUtils.getAsString(channelElement); +// if (channelId.contains("ActivePower")) { +// String name = thingId + "/" + channelId; +// boolean isGridOrStorage = false; +// for (int i = 0; i < things.size(); i++) { +// if (JsonUtils.getAsString(things.get(i)).equals(name)) { +// isGridOrStorage = true; +// } +// } +// if (!isGridOrStorage) { +// channelAddresses.add(thingId + "/" + channelId); +// } +// } +// } +// } +// return channelAddresses; +// } + + private static String toChannelAddressList(JsonObject channels) throws OpenemsException { + ArrayList channelAddresses = new ArrayList<>(); + for (Entry entry : channels.entrySet()) { + String thingId = entry.getKey(); + JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement channelElement : channelIds) { + String channelId = JsonUtils.getAsString(channelElement); + channelAddresses + .add("MEAN(\"" + thingId + "/" + channelId + "\") AS \"" + thingId + "/" + channelId + "\""); + } + } + return String.join(", ", channelAddresses); + } + + private static QueryResult executeQuery(InfluxDB influxdb, String database, String query) throws OpenemsException { + // Parse result + QueryResult queryResult; + try { + queryResult = influxdb.query(new Query(query, database), TimeUnit.MILLISECONDS); + } catch (RuntimeException e) { + throw new OpenemsException("InfluxDB query runtime error. Query: " + query + ", Error: " + e.getMessage()); + } + if (queryResult.hasError()) { + throw new OpenemsException("InfluxDB query error. Query: " + query + ", Error: " + queryResult.getError()); + } + return queryResult; + } +} diff --git a/common/src/io/openems/common/utils/JsonUtils.java b/common/src/io/openems/common/utils/JsonUtils.java new file mode 100644 index 00000000000..a866ea09b4c --- /dev/null +++ b/common/src/io/openems/common/utils/JsonUtils.java @@ -0,0 +1,342 @@ +package io.openems.common.utils; + +import java.net.Inet4Address; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +import io.openems.common.exceptions.NotImplementedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelEnum; + +// TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions +public class JsonUtils { + public static JsonArray getAsJsonArray(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonArray()) { + throw new OpenemsException("This is not a JsonArray: " + jElement); + } + return jElement.getAsJsonArray(); + }; + + public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jSubElement = getSubElement(jElement, memberName); + if (!jSubElement.isJsonArray()) { + throw new OpenemsException("Element [" + memberName + "] is not a JsonArray: " + jSubElement); + } + return jSubElement.getAsJsonArray(); + }; + + public static Optional getAsOptionalJsonArray(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsJsonArray(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonObject()) { + throw new OpenemsException("This is not a JsonObject: " + jElement); + } + return jElement.getAsJsonObject(); + }; + + public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jsubElement = getSubElement(jElement, memberName); + if (!jsubElement.isJsonObject()) { + throw new OpenemsException("Element [" + memberName + "] is not a JsonObject: " + jsubElement); + } + return jsubElement.getAsJsonObject(); + }; + + public static Optional getAsOptionalJsonObject(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsJsonObject(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jSubElement = getSubElement(jElement, memberName); + return getAsPrimitive(jSubElement); + } + + public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonPrimitive()) { + throw new OpenemsException("This is not a JsonPrimitive: " + jElement); + } + return jElement.getAsJsonPrimitive(); + } + + public static String getAsString(JsonElement jElement) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isString()) { + throw new OpenemsException("This is not a String: " + jPrimitive); + } + return jPrimitive.getAsString(); + } + + public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isBoolean()) { + throw new OpenemsException("This is not a Boolean: " + jPrimitive); + } + return jPrimitive.getAsBoolean(); + } + + public static Optional getAsOptionalString(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsString(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static String getAsString(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (!jPrimitive.isString()) { + throw new OpenemsException("Element [" + memberName + "] is not a String: " + jPrimitive); + } + return jPrimitive.getAsString(); + } + + public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsInt(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static Optional getAsOptionalLong(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsLong(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } + } + + public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsInt(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Integer.parseInt(string); + } + throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); + } + + public static boolean getAsBoolean(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (!jPrimitive.isBoolean()) { + throw new OpenemsException("Element [" + memberName + "] is not a Boolean: " + jPrimitive); + } + return jPrimitive.getAsBoolean(); + } + + /** + * Takes a json in the form 'YYYY-MM-DD' and converts it to a ZonedDateTime with + * hour, minute and second set to zero. + * + * @param jElement + * @param memberName + * @param timezone + * @return + * @throws OpenemsException + */ + public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone) + throws OpenemsException { + String[] date = JsonUtils.getAsString(jElement, memberName).split("-"); + try { + int year = Integer.valueOf(date[0]); + int month = Integer.valueOf(date[1]); + int day = Integer.valueOf(date[2]); + return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone); + } catch (ArrayIndexOutOfBoundsException e) { + throw new OpenemsException("Element [" + memberName + "] is not a Date: " + jElement + ". Error: " + e); + } + } + + public static long getAsLong(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsLong(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Long.parseLong(string); + } + throw new OpenemsException("[" + memberName + "] is not a Number: " + jPrimitive); + } + + public static JsonElement getSubElement(JsonElement jElement, String memberName) throws OpenemsException { + JsonObject jObject = getAsJsonObject(jElement); + if (!jObject.has(memberName)) { + throw new OpenemsException("Element [" + memberName + "] is not a Subelement of: " + jElement); + } + return jObject.get(memberName); + } + + /** + * Merges the second Object into the first object + * + * @param j1 + * @param j2 + * @return + */ + public static JsonObject merge(JsonObject j1, JsonObject j2) { + // TODO be smarter: merge down the tree + for (Entry entry : j2.entrySet()) { + j1.add(entry.getKey(), entry.getValue()); + } + return j1; + } + + public static Optional merge(Optional j1Opt, Optional j2Opt) { + if (j1Opt.isPresent() && j2Opt.isPresent()) { + return Optional.of(JsonUtils.merge(j1Opt.get(), j2Opt.get())); + } + if (j1Opt.isPresent()) { + return j1Opt; + } + return j2Opt; + } + + public static boolean hasElement(JsonElement j, String... paths) { + return getMatchingElements(j, paths).size() > 0; + } + + public static Set getMatchingElements(JsonElement j, String... paths) { + Set result = new HashSet(); + if (paths.length == 0) { + // last path element + result.add(j); + return result; + } + String path = paths[0]; + if (j.isJsonObject()) { + JsonObject jO = j.getAsJsonObject(); + if (jO.has(path)) { + List nextPathsList = new ArrayList(Arrays.asList(paths)); + nextPathsList.remove(0); + String[] nextPaths = nextPathsList.toArray(new String[0]); + result.addAll(getMatchingElements(jO.get(path), nextPaths)); + } + } else if (j.isJsonArray()) { + for (JsonElement jE : j.getAsJsonArray()) { + result.addAll(getMatchingElements(jE, paths)); + } + } else if (j.isJsonPrimitive()) { + JsonPrimitive jP = j.getAsJsonPrimitive(); + if (jP.isString()) { + if (jP.getAsString().equals(path)) { + result.add(jP); + } + } + } + return result; + } + + /** + * Pretty print a JsonElement + * + * @param j + */ + public static void prettyPrint(JsonElement j) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(j); + System.out.println(json); + } + + /** + * Parses a string to a JsonElement + * + * @param string + * @return + */ + public static JsonElement parse(String string) throws OpenemsException { + try { + JsonParser parser = new JsonParser(); + return parser.parse(string); + } catch (JsonParseException e) { + throw new OpenemsException("Unable to parse [" + string + "] + to JSON: " + e.getMessage(), e); + } + } + + /* + * Copied from edge TODO! + */ + public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { + // null + if (value == null) { + return null; + } + // optional + if (value instanceof Optional) { + if (!((Optional) value).isPresent()) { + return null; + } else { + value = ((Optional) value).get(); + } + } + if (value instanceof Number) { + /* + * Number + */ + return new JsonPrimitive((Number) value); + } else if(value instanceof ChannelEnum) { + /* + * ChannelEnum + */ + return new JsonPrimitive(((ChannelEnum)value).getValue()); + } else if (value instanceof String) { + /* + * String + */ + return new JsonPrimitive((String) value); + } else if (value instanceof Boolean) { + /* + * Boolean + */ + return new JsonPrimitive((Boolean) value); + } else if (value instanceof Inet4Address) { + /* + * Inet4Address + */ + return new JsonPrimitive(((Inet4Address) value).getHostAddress()); + } else if (value instanceof JsonElement) { + /* + * JsonElement + */ + return (JsonElement) value; + } else if (value instanceof Long[]){ + /* + * Long-Array + */ + JsonArray js = new JsonArray(); + for (Long l : (Long[]) value){ + js.add(new JsonPrimitive((Long) l)); + } + return js; + } + throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // + + value.getClass().getSimpleName() + "]" // + + " to JSON is not implemented."); + } +} diff --git a/common/src/io/openems/common/utils/SecureRandomSingleton.java b/common/src/io/openems/common/utils/SecureRandomSingleton.java new file mode 100644 index 00000000000..59325a1ac5e --- /dev/null +++ b/common/src/io/openems/common/utils/SecureRandomSingleton.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.common.utils; + +import java.security.SecureRandom; + +public class SecureRandomSingleton { + private static SecureRandom instance; + + public static synchronized SecureRandom getInstance() { + if (SecureRandomSingleton.instance == null) { + SecureRandomSingleton.instance = new SecureRandom(); + } + return SecureRandomSingleton.instance; + } + + private SecureRandomSingleton() {} +} diff --git a/common/src/io/openems/common/utils/StringUtils.java b/common/src/io/openems/common/utils/StringUtils.java new file mode 100644 index 00000000000..3522b85cf04 --- /dev/null +++ b/common/src/io/openems/common/utils/StringUtils.java @@ -0,0 +1,19 @@ +package io.openems.common.utils; + +import com.google.gson.JsonObject; + +public class StringUtils { + + public static String toShortString(JsonObject j, int length) { + String s = j.toString(); + if (s.length() > length - 3) { + return s.substring(0, length - 3) + "..."; + } else { + return s; + } + } + + public static String capitalizeFirstLetter(String s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } +} diff --git a/common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/common/src/io/openems/common/websocket/AbstractWebsocketServer.java index 07df2825e0d..b421929b02d 100644 --- a/common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ b/common/src/io/openems/common/websocket/AbstractWebsocketServer.java @@ -37,7 +37,6 @@ protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Opt protected abstract void _onClose(WebSocket websocket, Optional sessionOpt); - @SuppressWarnings("deprecation") public AbstractWebsocketServer(int port, M sessionManager) { super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); this.sessionManager = sessionManager; diff --git a/common/src/io/openems/common/websocket/CurrentDataWorker.java b/common/src/io/openems/common/websocket/CurrentDataWorker.java new file mode 100644 index 00000000000..941ea00b169 --- /dev/null +++ b/common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -0,0 +1,81 @@ +package io.openems.common.websocket; + +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.java_websocket.WebSocket; +import com.google.common.collect.HashMultimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.types.ChannelAddress; + +public abstract class CurrentDataWorker { + + protected final static int UPDATE_INTERVAL_IN_SECONDS = 2; + + /** + * Executor for subscriptions task + */ + private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + + /** + * Holds thingId and channelId, subscribed by this websocket + */ + private final HashMultimap channels; + + /** + * Holds the scheduled task for currentData + */ + private final ScheduledFuture future; + + public CurrentDataWorker(JsonArray jId, Optional deviceNameOpt, HashMultimap channels) { + this.channels = channels; + this.future = this.executor.scheduleWithFixedDelay(() -> { + /* + * This task is executed regularly. Sends data to websocket. + */ + Optional wsOpt = this.getWebsocket(); + if (!(wsOpt.isPresent() && wsOpt.get().isOpen())) { + // disconnected; stop worker + this.dispose(); + return; + } + WebSocketUtils.send(wsOpt.get(), DefaultMessages.currentData(jId, deviceNameOpt, getSubscribedData())); + }, 0, UPDATE_INTERVAL_IN_SECONDS, TimeUnit.SECONDS); + } + + public void dispose() { + // unsubscribe regular task + future.cancel(true); + } + + /** + * Gets a json object with all subscribed channels + * + * @return + */ + private JsonObject getSubscribedData() { + JsonObject jData = new JsonObject(); + for (String thingId : this.channels.keys()) { + JsonObject jThingData = new JsonObject(); + for (String channelId : this.channels.get(thingId)) { + ChannelAddress channelAddress = new ChannelAddress(thingId, channelId); + Optional jValueOpt = this.getChannelValue(channelAddress); + if (jValueOpt.isPresent()) { + jThingData.add(channelId, jValueOpt.get()); + } + } + jData.add(thingId, jThingData); + } + return jData; + } + + protected abstract Optional getChannelValue(ChannelAddress channelAddress); + + protected abstract Optional getWebsocket(); +} diff --git a/common/src/io/openems/common/websocket/DefaultMessages.java b/common/src/io/openems/common/websocket/DefaultMessages.java new file mode 100644 index 00000000000..85db6caef14 --- /dev/null +++ b/common/src/io/openems/common/websocket/DefaultMessages.java @@ -0,0 +1,357 @@ +package io.openems.common.websocket; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Optional; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.DeviceImpl; +import io.openems.common.types.FieldValue; +import io.openems.common.types.NumberFieldValue; +import io.openems.common.types.StringFieldValue; + +public class DefaultMessages { + + /** + *
        +	 *	{
        +	 *		authenticate: {
        +	 *			mode: "allow",
        +	 *			token: String
        +	 *		}, metadata: {
        +	 *			devices: [{
        +	 *				name: String,
        +	 *				comment: String,
        +	 *				producttype: String,
        +	 *				role: "admin" | "installer" | "owner" | "guest",
        +	 *				online: boolean
        +	 *			}]
        +	 *		}
        +	 *	}
        +	 * - authenticate.role is only sent for OpenEMS Edge
        +	 * - metadata.devices is only sent for OpenEMS Backend
        +	 * 
        + * + * @param token + * @return + */ + public static JsonObject browserConnectionSuccessfulReply(String token, Optional roleOpt, + Collection devices) { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "allow"); + if (roleOpt.isPresent()) { + jAuthenticate.addProperty("role", roleOpt.get()); + } + jAuthenticate.addProperty("token", token); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + JsonObject jMetadata = new JsonObject(); + if (!devices.isEmpty()) { + JsonArray jDevices = new JsonArray(); + for (DeviceImpl device : devices) { + JsonObject jDevice = new JsonObject(); + jDevice.addProperty("name", device.getName()); + jDevice.addProperty("comment", device.getComment()); + jDevice.addProperty("producttype", device.getProducttype()); + jDevice.addProperty("role", device.getRole().toString()); + jDevice.addProperty("online", device.isOnline()); + jDevices.add(jDevice); + } + jMetadata.add("devices", jDevices); + } + j.add("metadata", jMetadata); + return j; + } + + /** + *
        +	 *	{
        +	 *		authenticate: {
        +	 *			mode: "deny"
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @param token + * @return + */ + public static JsonObject browserConnectionFailedReply() { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "deny"); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + return j; + } + + /** + *
        +	 *	{
        +	 *		authenticate: {
        +	 *			mode: "allow"
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @param token + * @return + */ + public static JsonObject openemsConnectionSuccessfulReply() { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "allow"); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + return j; + } + + /** + *
        +	 *	{
        +	 *		authenticate: {
        +	 *			mode: "deny",
        +	 *			message: String
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @param token + * @return + */ + public static JsonObject openemsConnectionFailedReply(String message) { + JsonObject jAuthenticate = new JsonObject(); + jAuthenticate.addProperty("mode", "deny"); + jAuthenticate.addProperty("message", message); + JsonObject j = new JsonObject(); + j.add("authenticate", jAuthenticate); + return j; + } + + /** + *
        +	 *	{
        +	 *		timedata: {
        +	 *			timestamp (Long): {
        +	 *				channel: String,
        +	 *				value: String | Number
        +	 *			}
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @param token + * @return + */ + public static JsonObject timestampedData(long timestamp, HashMap> queue) { + JsonObject jTimestamp = new JsonObject(); + for (Entry> entry : queue.entrySet()) { + String address = entry.getKey().toString(); + FieldValue fieldValue = entry.getValue(); + if (fieldValue instanceof NumberFieldValue) { + jTimestamp.addProperty(address, ((NumberFieldValue) fieldValue).value); + } else if (fieldValue instanceof StringFieldValue) { + jTimestamp.addProperty(address, ((StringFieldValue) fieldValue).value); + } + } + JsonObject jTimedata = new JsonObject(); + jTimedata.add(String.valueOf(timestamp), jTimestamp); + JsonObject j = new JsonObject(); + j.add("timedata", jTimedata); + return j; + } + + /** + *
        +	 *	{
        +	 *		config: {
        +	 *			...
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @param token + * @return + */ + public static JsonObject configQueryReply(JsonObject config) { + JsonObject j = new JsonObject(); + j.add("config", config); + return j; + } + + /** + *
        +	 *	{
        +	 *		id: [string],
        +	 *		currentData: {[{ 
        +	 *			channel: string,
        +	 *			value: any
        +	 *		}]}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject currentData(JsonArray jId, Optional deviceNameOpt, JsonObject jCurrentData) { + JsonObject j = new JsonObject(); + j.add("id", jId); + if(deviceNameOpt.isPresent()) { + j.addProperty("device", deviceNameOpt.get()); + } + j.add("currentData", jCurrentData); + return j; + } + + /** + *
        +	 *	{
        +	 *		id: [string]
        +	 *		historicData: {
        +	 *			data: [{
        +	 *				time: ...,
        +	 *				channels: {
        +	 *					thing: {
        +	 *						channel: any
        +	 *					} 
        +	 *				}
        +	 *			}]
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject historicDataQueryReply(JsonArray jId, JsonArray jData) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jHistoricData = new JsonObject(); + jHistoricData.add("data", jData); + j.add("historicData", jHistoricData); + return j; + } + + /** + *
        +	 *	{
        +	 *		notification: {
        +	 *			id: string[],
        +	 *			type: string,
        +	 *			message: string,
        +	 *			code: number,
        +	 *			params: string[]
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject notification(JsonArray jId, Notification code, String message, Object... params) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jNotification = new JsonObject(); + jNotification.addProperty("type", code.getType().toString().toLowerCase()); + jNotification.addProperty("message", message); + jNotification.addProperty("code", code.getValue()); + JsonArray jParams = new JsonArray(); + for (Object param : params) { + jParams.add(param.toString()); + } + jNotification.add("params", jParams); + j.add("notification", jNotification); + return j; + } + + /** + *
        +	 *	{
        +	 *		currentData: {
        +	 *			mode: 'subscribe',
        +	 *			channels: {}
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannels) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jCurrentData = new JsonObject(); + jCurrentData.addProperty("mode", "subscribe"); + jCurrentData.add("channels", jChannels); + j.add("currentData", jCurrentData); + return j; + } + + /** + *
        +	 *	{
        +	 *		id: [string],
        +	 *		log: {
        +	 *			times: number,
        +	 *			level: string,
        +	 *			source: string,
        +	 *			message: string
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject log(JsonArray jId, long timestamp, String level, String source, String message) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jLog = new JsonObject(); + jLog.addProperty("time", timestamp); + jLog.addProperty("level", level); + jLog.addProperty("source", source); + jLog.addProperty("message", message); + j.add("log", jLog); + return j; + } + + /** + *
        +	 *	{
        +	 *		id: [string],
        +	 *		log: {
        +	 *			mode: "unsubscribe"
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject logUnsubscribe(JsonArray jId) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jLog = new JsonObject(); + jLog.addProperty("mode", "unsubscribe"); + j.add("log", jLog); + return j; + } + + /** + *
        +	 *	{
        +	 *		id: [string],
        +	 *		system: {
        +	 *			mode: "executeReply",
        +	 *			output: string
        +	 *		}
        +	 *	}
        +	 * 
        + * + * @return + */ + public static JsonObject systemExecuteReply(JsonArray jId, String output) { + JsonObject j = new JsonObject(); + j.add("id", jId); + JsonObject jSystem = new JsonObject(); + jSystem.addProperty("mode", "executeReply"); + jSystem.addProperty("output", output); + j.add("system", jSystem); + return j; + } +} diff --git a/common/src/io/openems/common/websocket/LogBehaviour.java b/common/src/io/openems/common/websocket/LogBehaviour.java new file mode 100644 index 00000000000..c74395a7b5c --- /dev/null +++ b/common/src/io/openems/common/websocket/LogBehaviour.java @@ -0,0 +1,5 @@ +package io.openems.common.websocket; + +public enum LogBehaviour { + WRITE_TO_LOG, DO_NOT_WRITE_TO_LOG; +} diff --git a/common/src/io/openems/common/websocket/Notification.java b/common/src/io/openems/common/websocket/Notification.java new file mode 100644 index 00000000000..fb3f72b4270 --- /dev/null +++ b/common/src/io/openems/common/websocket/Notification.java @@ -0,0 +1,54 @@ +package io.openems.common.websocket; + +import org.slf4j.Logger; + +public enum Notification { + EDGE_CONNECTION_ClOSED(100, NotificationType.WARNING, "Connection [%s] was interrupted"), + EDGE_CONNECTION_OPENED(101, NotificationType.INFO, "Connection [%s] was established"), + EDGE_UNABLE_TO_FORWARD(102, NotificationType.ERROR, "Unable to forward command to [%s]: %s"), + EDGE_AUTHENTICATION_BY_TOKEN_FAILED(103, NotificationType.INFO, "Authentication by token [%s] failed"), + EDGE_CHANNEL_UPDATE_SUCCESS(104, NotificationType.SUCCESS, "Configuration successfully updated [%s]"), + EDGE_CHANNEL_UPDATE_FAILED(105, NotificationType.ERROR, "Configuration update failed [%s]: %s"), + BACKEND_NOT_ALLOWED(106, NotificationType.ERROR, "The operation [%s] is not allowed via Backend"), + EDGE_CHANNEL_UPDATE_TIMEOUT(107, NotificationType.INFO, "Channel setting [%s] timed out."); + + private final int value; + private final NotificationType status; + private final String message; + + private Notification(int value, NotificationType status, String message) { + this.value = value; + this.status = status; + this.message = message; + } + + public int getValue() { + return value; + } + + public NotificationType getType() { + return status; + } + + public String getMessage() { + return message; + } + + public void writeToLog(Logger log, Object... params) { + String message = String.format(this.message, params); + String logMessage = "Notification [" + this.value + "]: " + message; + switch (this.status) { + case INFO: + case LOG: + case SUCCESS: + log.info(logMessage); + break; + case ERROR: + log.error(logMessage); + break; + case WARNING: + log.warn(logMessage); + break; + } + } +} diff --git a/common/src/io/openems/common/websocket/NotificationType.java b/common/src/io/openems/common/websocket/NotificationType.java new file mode 100644 index 00000000000..4d087b1e5b3 --- /dev/null +++ b/common/src/io/openems/common/websocket/NotificationType.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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.common.websocket; + +public enum NotificationType { + SUCCESS, ERROR, WARNING, INFO, LOG; +} diff --git a/common/src/io/openems/common/websocket/WebSocketUtils.java b/common/src/io/openems/common/websocket/WebSocketUtils.java new file mode 100644 index 00000000000..7072b091f95 --- /dev/null +++ b/common/src/io/openems/common/websocket/WebSocketUtils.java @@ -0,0 +1,113 @@ +package io.openems.common.websocket; + +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.exceptions.WebsocketNotConnectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.openems.common.api.TimedataSource; +import io.openems.common.session.Role; +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.StringUtils; + +public class WebSocketUtils { + + private static Logger log = LoggerFactory.getLogger(WebSocketUtils.class); + + public static boolean send(Optional websocketOpt, JsonObject j) { + if (!websocketOpt.isPresent()) { + log.error("Websocket is not available. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; + } else { + return WebSocketUtils.send(websocketOpt.get(), j); + } + } + + public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour, Notification code, + Object... params) { + if (!websocketOpt.isPresent()) { + log.error("Websocket is not available. Unable to send Notification [" + + String.format(code.getMessage(), params) + "]"); + return false; + } else { + return WebSocketUtils.sendNotification(websocketOpt.get(), jId, logBehaviour, code, params); + } + } + + public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour, Notification notification, Object... params) { + if (logBehaviour.equals(LogBehaviour.WRITE_TO_LOG)) { + // log message + notification.writeToLog(log, params); + } + String message = String.format(notification.getMessage(), params); + JsonObject j = DefaultMessages.notification(jId, notification, message, params); + return WebSocketUtils.send(websocket, j); + } + + /** + * Send a message to a websocket + * + * @param j + * @return true if successful, otherwise false + */ + public static boolean send(WebSocket websocket, JsonObject j) { + // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); + try { + websocket.send(j.toString()); + return true; + } catch (WebsocketNotConnectedException e) { + log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); + return false; + } + } + + /** + * Query history command + * + * @param j + */ + public static JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, + TimedataSource timedataSource, Role role) { + try { + String mode = JsonUtils.getAsString(jHistoricData, "mode"); + if (mode.equals("query")) { + /* + * Query historic data + */ + int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); + ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); + ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); + ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); + JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); + // TODO check if role is allowed to read these channels + // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); + int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); + // TODO: better calculation of sensible resolution + int resolution = 10 * 60; // 10 Minutes + if (days > 25) { + resolution = 24 * 60 * 60; // 1 Day + } else if (days > 6) { + resolution = 3 * 60 * 60; // 3 Hours + } else if (days > 2) { + resolution = 60 * 60; // 60 Minutes + } + JsonArray jData = timedataSource.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); + // send reply + return DefaultMessages.historicDataQueryReply(jMessageId, jData); + } + } catch (Exception e) { + log.error("HistoricData Error: ", e); + e.printStackTrace(); + } + return new JsonObject(); + } +} 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 f22614e80d5..c98c78deeea 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -22,7 +22,6 @@ import io.openems.common.exceptions.NotImplementedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.ChannelEnum; // TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions public class JsonUtils { @@ -308,11 +307,6 @@ public static JsonElement getAsJsonElement(Object value) throws NotImplementedEx * Number */ return new JsonPrimitive((Number) value); - } else if(value instanceof ChannelEnum) { - /* - * ChannelEnum - */ - return new JsonPrimitive(((ChannelEnum)value).getValue()); } else if (value instanceof String) { /* * String From 558c3f058fb50d702e700b0287a8daeee5d3bc25 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 24 Feb 2018 22:40:30 +0100 Subject: [PATCH 097/156] Remove old "common" package --- common/.classpath | 15 - common/.gitignore | 13 - common/.project | 23 - .../org.eclipse.core.resources.prefs | 3 - common/.settings/org.eclipse.jdt.core.prefs | 13 - common/.settings/org.eclipse.m2e.core.prefs | 4 - common/pom.xml | 78 - .../io/openems/common/api/TimedataSource.java | 36 - .../exceptions/NotImplementedException.java | 30 - .../common/exceptions/OpenemsException.java | 13 - .../src/io/openems/common/session/Role.java | 23 - .../io/openems/common/session/Session.java | 28 - .../openems/common/session/SessionData.java | 7 - .../common/session/SessionManager.java | 126 -- .../openems/common/types/ChannelAddress.java | 43 - .../src/io/openems/common/types/Device.java | 21 - .../io/openems/common/types/DeviceImpl.java | 61 - .../io/openems/common/types/FieldValue.java | 29 - .../openems/common/types/NullFieldValue.java | 29 - .../common/types/NumberFieldValue.java | 29 - .../common/types/StringFieldValue.java | 29 - .../common/types/TimestampedFieldValue.java | 19 - .../src/io/openems/common/utils/EnvUtils.java | 33 - .../openems/common/utils/InfluxdbUtils.java | 311 ---- .../io/openems/common/utils/JsonUtils.java | 342 ----- .../common/utils/SecureRandomSingleton.java | 36 - .../io/openems/common/utils/StringUtils.java | 19 - .../websocket/AbstractWebsocketServer.java | 193 --- .../common/websocket/CurrentDataWorker.java | 81 - .../common/websocket/DefaultMessages.java | 357 ----- .../common/websocket/LogBehaviour.java | 5 - .../common/websocket/Notification.java | 54 - .../common/websocket/NotificationType.java | 25 - .../common/websocket/WebSocketUtils.java | 113 -- edge/.classpath | 38 +- edge/pom.xml | 5 - edge/src/io/openems/api/doc/ChannelInfo.java | 154 +- .../api/persistence/QueryablePersistence.java | 69 +- .../websocket/EdgeCurrentDataWorker.java | 14 +- .../websocket/EdgeWebsocketHandler.java | 1349 +++++++++-------- .../api/websocket/WebsocketApiServer.java | 384 +++-- .../fenecon/FeneconPersistence.java | 2 +- .../influxdb/InfluxdbPersistence.java | 6 +- .../io/openems/backend/metadata/api/User.java | 2 + .../openems/backend/metadata/odoo/Odoo.java | 2 +- .../provider/BackendCurrentDataWorker.java | 5 +- .../impl/provider/UiWebsocketServer.java | 8 +- .../exceptions/AccessDeniedException.java | 0 .../src/io/openems/common/session}/Role.java | 2 +- .../io/openems/common/types/ChannelEnum.java | 0 .../io/openems/common/types/DeviceImpl.java | 2 +- .../io/openems/common/utils/JsonUtils.java | 6 + .../common/websocket/CurrentDataWorker.java | 4 +- .../common/websocket/DefaultMessages.java | 55 +- .../common/websocket/WebSocketUtils.java | 2 +- 55 files changed, 1059 insertions(+), 3291 deletions(-) delete mode 100644 common/.classpath delete mode 100644 common/.gitignore delete mode 100644 common/.project delete mode 100644 common/.settings/org.eclipse.core.resources.prefs delete mode 100644 common/.settings/org.eclipse.jdt.core.prefs delete mode 100644 common/.settings/org.eclipse.m2e.core.prefs delete mode 100644 common/pom.xml delete mode 100644 common/src/io/openems/common/api/TimedataSource.java delete mode 100644 common/src/io/openems/common/exceptions/NotImplementedException.java delete mode 100644 common/src/io/openems/common/exceptions/OpenemsException.java delete mode 100644 common/src/io/openems/common/session/Role.java delete mode 100644 common/src/io/openems/common/session/Session.java delete mode 100644 common/src/io/openems/common/session/SessionData.java delete mode 100644 common/src/io/openems/common/session/SessionManager.java delete mode 100644 common/src/io/openems/common/types/ChannelAddress.java delete mode 100644 common/src/io/openems/common/types/Device.java delete mode 100644 common/src/io/openems/common/types/DeviceImpl.java delete mode 100644 common/src/io/openems/common/types/FieldValue.java delete mode 100644 common/src/io/openems/common/types/NullFieldValue.java delete mode 100644 common/src/io/openems/common/types/NumberFieldValue.java delete mode 100644 common/src/io/openems/common/types/StringFieldValue.java delete mode 100644 common/src/io/openems/common/types/TimestampedFieldValue.java delete mode 100644 common/src/io/openems/common/utils/EnvUtils.java delete mode 100644 common/src/io/openems/common/utils/InfluxdbUtils.java delete mode 100644 common/src/io/openems/common/utils/JsonUtils.java delete mode 100644 common/src/io/openems/common/utils/SecureRandomSingleton.java delete mode 100644 common/src/io/openems/common/utils/StringUtils.java delete mode 100644 common/src/io/openems/common/websocket/AbstractWebsocketServer.java delete mode 100644 common/src/io/openems/common/websocket/CurrentDataWorker.java delete mode 100644 common/src/io/openems/common/websocket/DefaultMessages.java delete mode 100644 common/src/io/openems/common/websocket/LogBehaviour.java delete mode 100644 common/src/io/openems/common/websocket/Notification.java delete mode 100644 common/src/io/openems/common/websocket/NotificationType.java delete mode 100644 common/src/io/openems/common/websocket/WebSocketUtils.java rename {common => io.openems.common}/src/io/openems/common/exceptions/AccessDeniedException.java (100%) rename {io.openems.backend.metadata.api/src/io/openems/backend/metadata/api => io.openems.common/src/io/openems/common/session}/Role.java (91%) rename {common => io.openems.common}/src/io/openems/common/types/ChannelEnum.java (100%) diff --git a/common/.classpath b/common/.classpath deleted file mode 100644 index a4379df1766..00000000000 --- a/common/.classpath +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/common/.gitignore b/common/.gitignore deleted file mode 100644 index 4b824fb52ad..00000000000 --- a/common/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -/target/ -/openems diff --git a/common/.project b/common/.project deleted file mode 100644 index d546e76feba..00000000000 --- a/common/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - common - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/common/.settings/org.eclipse.core.resources.prefs b/common/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 7a531392842..00000000000 --- a/common/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 -encoding/src=UTF-8 diff --git a/common/.settings/org.eclipse.jdt.core.prefs b/common/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 529ef073a4f..00000000000 --- a/common/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/common/.settings/org.eclipse.m2e.core.prefs b/common/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index 14b697b7bbb..00000000000 --- a/common/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/common/pom.xml b/common/pom.xml deleted file mode 100644 index 063076c4593..00000000000 --- a/common/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ - - OpenEMS Common - Open Source Energy Management System - http://openems.io - io.openems - common - 2018.2.0-SNAPSHOT - jar - - https://github.com/OpenEMS/openems - scm:git:git://github.com/OpenEMS/openems.git - - - 1.8 - 1.8 - UTF-8 - 2.8.2 - 23.1-jre - 2.7 - 4.8.1 - 1.2.3 - 2.3.10 - 1.1.0.RELEASE - 1.7.25 - 1.3.6 - - - - maven-restlet - Public online Restlet repository - http://maven.restlet.com - - - - - com.google.code.gson - gson - ${gson.version} - - - com.google.guava - guava - ${guava.version} - - - org.influxdb - influxdb-java - ${influxdb.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - org.restlet.jse - org.restlet - ${restlet.version} - - - org.restlet.jse - org.restlet.ext.slf4j - ${restlet.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.java-websocket - Java-WebSocket - ${websocket.version} - - - 4.0.0 - diff --git a/common/src/io/openems/common/api/TimedataSource.java b/common/src/io/openems/common/api/TimedataSource.java deleted file mode 100644 index 0744d3a81d4..00000000000 --- a/common/src/io/openems/common/api/TimedataSource.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.openems.common.api; - -import java.time.ZonedDateTime; -import java.util.Optional; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; - -public interface TimedataSource { - /** - * Queries the database and returns a JsonArray of the form - * - *
        -	 *	[{
        -	 *  	timestamp: "2017-03-21T08:55:20Z",
        -	 *  	channels: {
        -	 *			'thing': {
        -	 *				'channel': 'value'
        -	 *			}
        -	 *		}
        -	 * 	}]
        -	 * 
        - * - * @param deviceId - * @param fromDate - * @param toDate - * @param channels - * @param resolution - * @return - * @throws OpenemsException - */ - public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, - int resolution/* , JsonObject kWh */) throws OpenemsException; -} diff --git a/common/src/io/openems/common/exceptions/NotImplementedException.java b/common/src/io/openems/common/exceptions/NotImplementedException.java deleted file mode 100644 index 1990b3690fc..00000000000 --- a/common/src/io/openems/common/exceptions/NotImplementedException.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.exceptions; - -public class NotImplementedException extends OpenemsException { - - private static final long serialVersionUID = 1L; - - public NotImplementedException(String message) { - super(message); - } -} diff --git a/common/src/io/openems/common/exceptions/OpenemsException.java b/common/src/io/openems/common/exceptions/OpenemsException.java deleted file mode 100644 index bea21e0044b..00000000000 --- a/common/src/io/openems/common/exceptions/OpenemsException.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.openems.common.exceptions; - -public class OpenemsException extends Exception { - private static final long serialVersionUID = 5015013132334439401L; - - public OpenemsException(String message) { - super(message); - } - - public OpenemsException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/common/src/io/openems/common/session/Role.java b/common/src/io/openems/common/session/Role.java deleted file mode 100644 index 3d6c44e55ca..00000000000 --- a/common/src/io/openems/common/session/Role.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.openems.common.session; - -public enum Role { - ADMIN, INSTALLER, OWNER, GUEST; - - public static Role getRole(String name) { - switch (name.toLowerCase()) { - case "admin": - return ADMIN; - case "installer": - return INSTALLER; - case "owner": - return OWNER; - case "guest": - default: - return GUEST; - } - } - - public static Role getDefaultRole() { - return GUEST; - } -} diff --git a/common/src/io/openems/common/session/Session.java b/common/src/io/openems/common/session/Session.java deleted file mode 100644 index 829266059d7..00000000000 --- a/common/src/io/openems/common/session/Session.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.openems.common.session; - -public class Session { - private final String token; - - /** - * store additional metadata to this session - */ - private final D data; - - protected Session(String token, D data) { - this.token = token; - this.data = data; - } - - public String getToken() { - return token; - } - - public D getData() { - return data; - } - - @Override - public String toString() { - return "Session [token=" + token + ", data=" + data + "]"; - } -} diff --git a/common/src/io/openems/common/session/SessionData.java b/common/src/io/openems/common/session/SessionData.java deleted file mode 100644 index bd2b6b1aff6..00000000000 --- a/common/src/io/openems/common/session/SessionData.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.openems.common.session; - -import com.google.gson.JsonObject; - -public abstract class SessionData { - public abstract JsonObject toJsonObject(); -} diff --git a/common/src/io/openems/common/session/SessionManager.java b/common/src/io/openems/common/session/SessionManager.java deleted file mode 100644 index 2452d5f5224..00000000000 --- a/common/src/io/openems/common/session/SessionManager.java +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.session; - -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.utils.SecureRandomSingleton; - -public abstract class SessionManager, D extends SessionData> { - - private final Logger log = LoggerFactory.getLogger(SessionManager.class); - private final static int SESSION_ID_LENGTH = 130; - - // TODO: invalidate old sessions in separate thread: call _removeSession to do - // so - private final Map sessions = new ConcurrentHashMap<>(); - - protected SessionManager() { - } - - public S createNewSession(String token, D data) { - S session = this._createNewSession(token, data); - this._putSession(token, session); - return session; - } - - public S createNewSession(D data) { - String token = this.generateToken(); - return this.createNewSession(token, data); - } - - public Optional getSessionByToken(String token) { - synchronized (this.sessions) { - return Optional.ofNullable(this.sessions.get(token)); - } - } - - public void removeSession(String token) { - synchronized (this.sessions) { - S session = this.sessions.get(token); - if (session != null) { - this._removeSession(token); - } - } - } - - public void removeSession(Session session) { - this.removeSession(session.getToken()); - } - - protected String generateToken() { - // Source: http://stackoverflow.com/a/41156 - SecureRandom sr = SecureRandomSingleton.getInstance(); - return new BigInteger(SESSION_ID_LENGTH, sr).toString(32); - } - - public Collection getSessions() { - return Collections.unmodifiableCollection(this.sessions.values()); - } - - /* - * Those methods are prone to be overwritten by inheritance - */ - /** - * Replies a Session object of type T - * - * @param token - * @param websocket - * @param data - * @return - */ - protected abstract S _createNewSession(String token, D data); - - /** - * This method is always called when adding a session to local database - * - * @param token - * @param session - */ - protected void _putSession(String token, S session) { - synchronized (this.sessions) { - if (this.sessions.containsKey(token)) { - log.warn("Session with token [" + token + "] already existed. Replacing with session [" + session + "]"); - } - this.sessions.put(token, session); - } - } - - /** - * This method is always called when removing a session from local database - * - * @param session - */ - protected void _removeSession(String token) { - synchronized (this.sessions) { - this.sessions.remove(token); - } - } -} diff --git a/common/src/io/openems/common/types/ChannelAddress.java b/common/src/io/openems/common/types/ChannelAddress.java deleted file mode 100644 index a5f805e9ef0..00000000000 --- a/common/src/io/openems/common/types/ChannelAddress.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.openems.common.types; - -import io.openems.common.exceptions.OpenemsException; - -public class ChannelAddress implements Comparable { - private final String thingId; - private final String channelId; - - public ChannelAddress(String thingId, String channelId) { - super(); - this.thingId = thingId; - this.channelId = channelId; - } - - public String getThingId() { - return thingId; - } - - public String getChannelId() { - return channelId; - } - - @Override - public String toString() { - return thingId + "/" + channelId; - } - - public static ChannelAddress fromString(String address) throws OpenemsException { - try { - String[] addressArray = address.split("/"); - String thingId = addressArray[0]; - String channelId = addressArray[1]; - return new ChannelAddress(thingId, channelId); - } catch (Exception e) { - throw new OpenemsException("This [" + address + "] is not a valid channel address."); - } - } - - @Override - public int compareTo(ChannelAddress other) { - return this.toString().compareTo(other.toString()); - } -} diff --git a/common/src/io/openems/common/types/Device.java b/common/src/io/openems/common/types/Device.java deleted file mode 100644 index 6bf2309f41b..00000000000 --- a/common/src/io/openems/common/types/Device.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.openems.common.types; - -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public interface Device { - - final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); - - public Optional getIdOpt(); - - public static Optional parseNumberFromName(String name) { - Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); - if (matcher.find()) { - String nameNumberString = matcher.group(1); - return Optional.ofNullable(Integer.parseInt(nameNumberString)); - } - return Optional.empty(); - } -} diff --git a/common/src/io/openems/common/types/DeviceImpl.java b/common/src/io/openems/common/types/DeviceImpl.java deleted file mode 100644 index 124d8e721a9..00000000000 --- a/common/src/io/openems/common/types/DeviceImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.openems.common.types; - -import java.util.Optional; - -import io.openems.common.session.Role; - -/** - * Helper class to store tuple of device name and role - * - * @author stefan.feilmeier - * - */ -public class DeviceImpl implements Comparable, Device { - private final String name; - private final String comment; - private final String producttype; - private final Role role; - private boolean online = false; - - public DeviceImpl(String name, String comment, String producttype, String role) { - this.name = name; - this.comment = comment; - this.producttype = producttype; - this.role = Role.getRole(role); - } - - public String getName() { - return name; - } - - public Role getRole() { - return role; - } - - public void setOnline(boolean online) { - this.online = online; - } - - public boolean isOnline() { - return online; - } - - public String getComment() { - return comment; - } - - public String getProducttype() { - return producttype; - } - - @Override - public int compareTo(DeviceImpl other) { - return (this.name + this.comment + this.producttype + this.role.toString() + this.online) - .compareTo(other.name + other.comment + other.producttype + other.role.toString() + other.online); - } - - @Override - public Optional getIdOpt() { - return Device.parseNumberFromName(this.getName()); - } -} diff --git a/common/src/io/openems/common/types/FieldValue.java b/common/src/io/openems/common/types/FieldValue.java deleted file mode 100644 index aac1c08db2e..00000000000 --- a/common/src/io/openems/common/types/FieldValue.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public abstract class FieldValue { - public final T value; - - public FieldValue(T value) { - this.value = value; - } -} diff --git a/common/src/io/openems/common/types/NullFieldValue.java b/common/src/io/openems/common/types/NullFieldValue.java deleted file mode 100644 index 3e889aa8636..00000000000 --- a/common/src/io/openems/common/types/NullFieldValue.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public class NullFieldValue extends FieldValue { - - public NullFieldValue() { - super(null); - } - -} diff --git a/common/src/io/openems/common/types/NumberFieldValue.java b/common/src/io/openems/common/types/NumberFieldValue.java deleted file mode 100644 index 355804fc9a9..00000000000 --- a/common/src/io/openems/common/types/NumberFieldValue.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public class NumberFieldValue extends FieldValue { - - public NumberFieldValue(Number value) { - super(value); - } - -} diff --git a/common/src/io/openems/common/types/StringFieldValue.java b/common/src/io/openems/common/types/StringFieldValue.java deleted file mode 100644 index 9ed25e24d93..00000000000 --- a/common/src/io/openems/common/types/StringFieldValue.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.types; - -public class StringFieldValue extends FieldValue { - - public StringFieldValue(String value) { - super(value); - } - -} diff --git a/common/src/io/openems/common/types/TimestampedFieldValue.java b/common/src/io/openems/common/types/TimestampedFieldValue.java deleted file mode 100644 index a2837de8149..00000000000 --- a/common/src/io/openems/common/types/TimestampedFieldValue.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.openems.common.types; - -public class TimestampedFieldValue { - private long timestamp; - private FieldValue value; - - public TimestampedFieldValue(long timestamp, FieldValue value) { - this.timestamp = timestamp; - this.value = value; - } - - public long getTimestamp() { - return timestamp; - } - - public FieldValue getValue() { - return value; - } -} diff --git a/common/src/io/openems/common/utils/EnvUtils.java b/common/src/io/openems/common/utils/EnvUtils.java deleted file mode 100644 index 9836e007d67..00000000000 --- a/common/src/io/openems/common/utils/EnvUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.openems.common.utils; - -import java.util.Optional; - -import io.openems.common.exceptions.OpenemsException; - -public class EnvUtils { - public static boolean isSet(String name) { - return System.getenv(name) != null; - }; - - public static String getAsString(String name) throws OpenemsException { - String value = System.getenv(name); - if (value == null || value.isEmpty()) { - throw new OpenemsException("ENV [" + name + "] is not set"); - } - return value; - }; - - public static Optional getAsOptionalString(String name) { - String value = System.getenv(name); - return Optional.ofNullable(value); - }; - - public static int getAsInt(String name) throws OpenemsException { - String valueString = EnvUtils.getAsString(name); - try { - return Integer.valueOf(valueString); - } catch (NumberFormatException e) { - throw new OpenemsException("ENV [" + name + "] value [" + valueString + "] is not an integer"); - } - }; -} diff --git a/common/src/io/openems/common/utils/InfluxdbUtils.java b/common/src/io/openems/common/utils/InfluxdbUtils.java deleted file mode 100644 index 656ac49319f..00000000000 --- a/common/src/io/openems/common/utils/InfluxdbUtils.java +++ /dev/null @@ -1,311 +0,0 @@ -package io.openems.common.utils; - -import java.time.Instant; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import org.influxdb.InfluxDB; -import org.influxdb.dto.Query; -import org.influxdb.dto.QueryResult; -import org.influxdb.dto.QueryResult.Result; -import org.influxdb.dto.QueryResult.Series; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.ChannelAddress; - -public class InfluxdbUtils { - - public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional deviceId, - ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { - // Prepare query string - StringBuilder query = new StringBuilder("SELECT "); - query.append(toChannelAddressList(channels)); - query.append(" FROM data WHERE "); - if (deviceId.isPresent()) { - query.append("fems = '"); - query.append(deviceId.get()); - query.append("' AND "); - } - query.append("time > "); - query.append(String.valueOf(fromDate.toEpochSecond())); - query.append("s"); - query.append(" AND time < "); - query.append(String.valueOf(toDate.toEpochSecond())); - query.append("s"); - query.append(" GROUP BY time("); - query.append(resolution); - query.append("s) fill(null)"); - - QueryResult queryResult = executeQuery(influxdb, database, query.toString()); - - JsonArray j = new JsonArray(); - for (Result result : queryResult.getResults()) { - List seriess = result.getSeries(); - if (seriess != null) { - for (Series series : seriess) { - // create thing/channel index - ArrayList addressIndex = new ArrayList<>(); - for (String column : series.getColumns()) { - if (column.equals("time")) { - continue; - } - addressIndex.add(ChannelAddress.fromString(column)); - } - // first: create empty timestamp objects - for (List values : series.getValues()) { - JsonObject jTimestamp = new JsonObject(); - // get timestamp - Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) values.get(0)).doubleValue()); - ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); - String timestampString = timestamp.format(DateTimeFormatter.ISO_INSTANT); - jTimestamp.addProperty("time", timestampString); - // add empty channels by copying "channels" parameter - JsonObject jChannels = new JsonObject(); - for (Entry entry : channels.entrySet()) { - String thingId = entry.getKey(); - JsonObject jThing = new JsonObject(); - JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement channelElement : channelIds) { - String channelId = JsonUtils.getAsString(channelElement); - jThing.add(channelId, JsonNull.INSTANCE); - } - jChannels.add(thingId, jThing); - } - jTimestamp.add("channels", jChannels); - j.add(jTimestamp); - } - // then: add all data - for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { - for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { - Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); - ChannelAddress address = addressIndex.get(columnIndex - 1); - j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() - .get(address.getThingId()).getAsJsonObject() - .addProperty(address.getChannelId(), value); - } - } - } - } - } - return j; - } - -// private static JsonObject querykWh(InfluxDB influxdb, String database, Optional fems, -// ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh) -// throws OpenemsException { -// JsonArray gridThing = getGridThing(kWh); -// JsonArray storageThing = getStorageThing(kWh); -// JsonArray things = new JsonArray(); -// things.addAll(storageThing); -// things.addAll(gridThing); -// -// JsonObject jThing = new JsonObject(); -// ArrayList productionChannels = toChannelAddressListAvg(channels, things); -// -// for (int i = 0; i < productionChannels.size(); i++) { -// /* -// * SUM data -// */ -// StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); -// query.append(productionChannels.get(i)); -// query.append("\") AS AP FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time > "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// query.append(" AND time < "); -// query.append(String.valueOf(toDate.toEpochSecond())); -// query.append("s"); -// query.append(" GROUP BY time(1s) fill(previous))"); -// -// QueryResult queryResult = executeQuery(influxdb, database, query.toString()); -// -// Double sumProduction = 0.0; -// try { -// for (Result result : queryResult.getResults()) { -// for (Series serie : result.getSeries()) { -// for (List l : serie.getValues()) { -// sumProduction = (Double) l.get(1); -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing SUM production: " + e); -// } -// -// /* -// * FIRST production data -// */ -// query = new StringBuilder("SELECT FIRST(\""); -// query.append(productionChannels.get(i)); -// query.append("\") FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time > "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// query.append(" AND time < "); -// query.append(String.valueOf(toDate.toEpochSecond())); -// query.append("s"); -// -// queryResult = executeQuery(influxdb, database, query.toString()); -// -// int second = 0; -// try { -// for (Result result : queryResult.getResults()) { -// for (Series serie : result.getSeries()) { -// for (List l : serie.getValues()) { -// Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); -// ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); -// if (timestamp.equals(fromDate)) { -// System.out.println("Parsing FIRST: nothing null"); -// } else { -// second = timestamp.getSecond(); -// } -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing FIRST production: " + e); -// } -// -// /* -// * LAST data -// */ -// query = new StringBuilder("SELECT LAST(\""); -// query.append(productionChannels.get(i)); -// query.append("\") FROM data WHERE "); -// if (fems.isPresent()) { -// query.append("fems = '"); -// query.append(fems.get()); -// query.append("' AND "); -// } -// query.append("time < "); -// query.append(String.valueOf(fromDate.toEpochSecond())); -// query.append("s"); -// -// queryResult = executeQuery(influxdb, query.toString(), database); -// -// try { -// if (queryResult.getResults() != null) { -// for (Result result : queryResult.getResults()) { -// if (result.getSeries() != null) { -// for (Series serie : result.getSeries()) { -// if (serie.getValues() != null) { -// for (List l : serie.getValues()) { -// if (l.get(1) != null) { -// sumProduction += (Double) l.get(1) * second; -// } -// } -// } -// } -// } -// } -// } -// } catch (Exception e) { -// System.out.println("Error parsing LAST production: " + e); -// } -// -// Double avg = sumProduction / 3600 / 1000; -// -// JsonObject element = new JsonObject(); -// element.addProperty("value", avg); -// element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); -// jThing.add(productionChannels.get(i).toString(), element); -// } -// -// return jThing; -// } - -// private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { -// JsonArray gridThing = new JsonArray(); -// for (Entry entry : kWh.entrySet()) { -// String thingId = entry.getKey(); -// if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { -// gridThing.add(thingId); -// } -// } -// return gridThing; -// } -// -// private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { -// JsonArray storageThing = new JsonArray(); -// for (Entry entry : kWh.entrySet()) { -// String thingId = entry.getKey(); -// if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { -// storageThing.add(thingId); -// } -// } -// return storageThing; -// } -// -// private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) -// throws OpenemsException { -// ArrayList channelAddresses = new ArrayList<>(); -// for (Entry entry : channels.entrySet()) { -// String thingId = entry.getKey(); -// JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); -// for (JsonElement channelElement : channelIds) { -// String channelId = JsonUtils.getAsString(channelElement); -// if (channelId.contains("ActivePower")) { -// String name = thingId + "/" + channelId; -// boolean isGridOrStorage = false; -// for (int i = 0; i < things.size(); i++) { -// if (JsonUtils.getAsString(things.get(i)).equals(name)) { -// isGridOrStorage = true; -// } -// } -// if (!isGridOrStorage) { -// channelAddresses.add(thingId + "/" + channelId); -// } -// } -// } -// } -// return channelAddresses; -// } - - private static String toChannelAddressList(JsonObject channels) throws OpenemsException { - ArrayList channelAddresses = new ArrayList<>(); - for (Entry entry : channels.entrySet()) { - String thingId = entry.getKey(); - JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement channelElement : channelIds) { - String channelId = JsonUtils.getAsString(channelElement); - channelAddresses - .add("MEAN(\"" + thingId + "/" + channelId + "\") AS \"" + thingId + "/" + channelId + "\""); - } - } - return String.join(", ", channelAddresses); - } - - private static QueryResult executeQuery(InfluxDB influxdb, String database, String query) throws OpenemsException { - // Parse result - QueryResult queryResult; - try { - queryResult = influxdb.query(new Query(query, database), TimeUnit.MILLISECONDS); - } catch (RuntimeException e) { - throw new OpenemsException("InfluxDB query runtime error. Query: " + query + ", Error: " + e.getMessage()); - } - if (queryResult.hasError()) { - throw new OpenemsException("InfluxDB query error. Query: " + query + ", Error: " + queryResult.getError()); - } - return queryResult; - } -} diff --git a/common/src/io/openems/common/utils/JsonUtils.java b/common/src/io/openems/common/utils/JsonUtils.java deleted file mode 100644 index a866ea09b4c..00000000000 --- a/common/src/io/openems/common/utils/JsonUtils.java +++ /dev/null @@ -1,342 +0,0 @@ -package io.openems.common.utils; - -import java.net.Inet4Address; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; - -import io.openems.common.exceptions.NotImplementedException; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.ChannelEnum; - -// TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions -public class JsonUtils { - public static JsonArray getAsJsonArray(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonArray()) { - throw new OpenemsException("This is not a JsonArray: " + jElement); - } - return jElement.getAsJsonArray(); - }; - - public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jSubElement = getSubElement(jElement, memberName); - if (!jSubElement.isJsonArray()) { - throw new OpenemsException("Element [" + memberName + "] is not a JsonArray: " + jSubElement); - } - return jSubElement.getAsJsonArray(); - }; - - public static Optional getAsOptionalJsonArray(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsJsonArray(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonObject()) { - throw new OpenemsException("This is not a JsonObject: " + jElement); - } - return jElement.getAsJsonObject(); - }; - - public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jsubElement = getSubElement(jElement, memberName); - if (!jsubElement.isJsonObject()) { - throw new OpenemsException("Element [" + memberName + "] is not a JsonObject: " + jsubElement); - } - return jsubElement.getAsJsonObject(); - }; - - public static Optional getAsOptionalJsonObject(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsJsonObject(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jSubElement = getSubElement(jElement, memberName); - return getAsPrimitive(jSubElement); - } - - public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonPrimitive()) { - throw new OpenemsException("This is not a JsonPrimitive: " + jElement); - } - return jElement.getAsJsonPrimitive(); - } - - public static String getAsString(JsonElement jElement) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isString()) { - throw new OpenemsException("This is not a String: " + jPrimitive); - } - return jPrimitive.getAsString(); - } - - public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isBoolean()) { - throw new OpenemsException("This is not a Boolean: " + jPrimitive); - } - return jPrimitive.getAsBoolean(); - } - - public static Optional getAsOptionalString(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsString(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static String getAsString(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isString()) { - throw new OpenemsException("Element [" + memberName + "] is not a String: " + jPrimitive); - } - return jPrimitive.getAsString(); - } - - public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsInt(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static Optional getAsOptionalLong(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsLong(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } - - public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsInt(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Integer.parseInt(string); - } - throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); - } - - public static boolean getAsBoolean(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isBoolean()) { - throw new OpenemsException("Element [" + memberName + "] is not a Boolean: " + jPrimitive); - } - return jPrimitive.getAsBoolean(); - } - - /** - * Takes a json in the form 'YYYY-MM-DD' and converts it to a ZonedDateTime with - * hour, minute and second set to zero. - * - * @param jElement - * @param memberName - * @param timezone - * @return - * @throws OpenemsException - */ - public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone) - throws OpenemsException { - String[] date = JsonUtils.getAsString(jElement, memberName).split("-"); - try { - int year = Integer.valueOf(date[0]); - int month = Integer.valueOf(date[1]); - int day = Integer.valueOf(date[2]); - return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone); - } catch (ArrayIndexOutOfBoundsException e) { - throw new OpenemsException("Element [" + memberName + "] is not a Date: " + jElement + ". Error: " + e); - } - } - - public static long getAsLong(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsLong(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Long.parseLong(string); - } - throw new OpenemsException("[" + memberName + "] is not a Number: " + jPrimitive); - } - - public static JsonElement getSubElement(JsonElement jElement, String memberName) throws OpenemsException { - JsonObject jObject = getAsJsonObject(jElement); - if (!jObject.has(memberName)) { - throw new OpenemsException("Element [" + memberName + "] is not a Subelement of: " + jElement); - } - return jObject.get(memberName); - } - - /** - * Merges the second Object into the first object - * - * @param j1 - * @param j2 - * @return - */ - public static JsonObject merge(JsonObject j1, JsonObject j2) { - // TODO be smarter: merge down the tree - for (Entry entry : j2.entrySet()) { - j1.add(entry.getKey(), entry.getValue()); - } - return j1; - } - - public static Optional merge(Optional j1Opt, Optional j2Opt) { - if (j1Opt.isPresent() && j2Opt.isPresent()) { - return Optional.of(JsonUtils.merge(j1Opt.get(), j2Opt.get())); - } - if (j1Opt.isPresent()) { - return j1Opt; - } - return j2Opt; - } - - public static boolean hasElement(JsonElement j, String... paths) { - return getMatchingElements(j, paths).size() > 0; - } - - public static Set getMatchingElements(JsonElement j, String... paths) { - Set result = new HashSet(); - if (paths.length == 0) { - // last path element - result.add(j); - return result; - } - String path = paths[0]; - if (j.isJsonObject()) { - JsonObject jO = j.getAsJsonObject(); - if (jO.has(path)) { - List nextPathsList = new ArrayList(Arrays.asList(paths)); - nextPathsList.remove(0); - String[] nextPaths = nextPathsList.toArray(new String[0]); - result.addAll(getMatchingElements(jO.get(path), nextPaths)); - } - } else if (j.isJsonArray()) { - for (JsonElement jE : j.getAsJsonArray()) { - result.addAll(getMatchingElements(jE, paths)); - } - } else if (j.isJsonPrimitive()) { - JsonPrimitive jP = j.getAsJsonPrimitive(); - if (jP.isString()) { - if (jP.getAsString().equals(path)) { - result.add(jP); - } - } - } - return result; - } - - /** - * Pretty print a JsonElement - * - * @param j - */ - public static void prettyPrint(JsonElement j) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(j); - System.out.println(json); - } - - /** - * Parses a string to a JsonElement - * - * @param string - * @return - */ - public static JsonElement parse(String string) throws OpenemsException { - try { - JsonParser parser = new JsonParser(); - return parser.parse(string); - } catch (JsonParseException e) { - throw new OpenemsException("Unable to parse [" + string + "] + to JSON: " + e.getMessage(), e); - } - } - - /* - * Copied from edge TODO! - */ - public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { - // null - if (value == null) { - return null; - } - // optional - if (value instanceof Optional) { - if (!((Optional) value).isPresent()) { - return null; - } else { - value = ((Optional) value).get(); - } - } - if (value instanceof Number) { - /* - * Number - */ - return new JsonPrimitive((Number) value); - } else if(value instanceof ChannelEnum) { - /* - * ChannelEnum - */ - return new JsonPrimitive(((ChannelEnum)value).getValue()); - } else if (value instanceof String) { - /* - * String - */ - return new JsonPrimitive((String) value); - } else if (value instanceof Boolean) { - /* - * Boolean - */ - return new JsonPrimitive((Boolean) value); - } else if (value instanceof Inet4Address) { - /* - * Inet4Address - */ - return new JsonPrimitive(((Inet4Address) value).getHostAddress()); - } else if (value instanceof JsonElement) { - /* - * JsonElement - */ - return (JsonElement) value; - } else if (value instanceof Long[]){ - /* - * Long-Array - */ - JsonArray js = new JsonArray(); - for (Long l : (Long[]) value){ - js.add(new JsonPrimitive((Long) l)); - } - return js; - } - throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // - + value.getClass().getSimpleName() + "]" // - + " to JSON is not implemented."); - } -} diff --git a/common/src/io/openems/common/utils/SecureRandomSingleton.java b/common/src/io/openems/common/utils/SecureRandomSingleton.java deleted file mode 100644 index 59325a1ac5e..00000000000 --- a/common/src/io/openems/common/utils/SecureRandomSingleton.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.utils; - -import java.security.SecureRandom; - -public class SecureRandomSingleton { - private static SecureRandom instance; - - public static synchronized SecureRandom getInstance() { - if (SecureRandomSingleton.instance == null) { - SecureRandomSingleton.instance = new SecureRandom(); - } - return SecureRandomSingleton.instance; - } - - private SecureRandomSingleton() {} -} diff --git a/common/src/io/openems/common/utils/StringUtils.java b/common/src/io/openems/common/utils/StringUtils.java deleted file mode 100644 index 3522b85cf04..00000000000 --- a/common/src/io/openems/common/utils/StringUtils.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.openems.common.utils; - -import com.google.gson.JsonObject; - -public class StringUtils { - - public static String toShortString(JsonObject j, int length) { - String s = j.toString(); - if (s.length() > length - 3) { - return s.substring(0, length - 3) + "..."; - } else { - return s; - } - } - - public static String capitalizeFirstLetter(String s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); - } -} diff --git a/common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/common/src/io/openems/common/websocket/AbstractWebsocketServer.java deleted file mode 100644 index b421929b02d..00000000000 --- a/common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ /dev/null @@ -1,193 +0,0 @@ -package io.openems.common.websocket; - -import java.net.InetSocketAddress; -import java.util.Iterator; -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.java_websocket.drafts.*; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import io.openems.common.session.Session; -import io.openems.common.session.SessionData; -import io.openems.common.session.SessionManager; -import io.openems.common.utils.JsonUtils; - -public abstract class AbstractWebsocketServer, D extends SessionData, M extends SessionManager> - extends WebSocketServer { - private final Logger log = LoggerFactory.getLogger(AbstractWebsocketServer.class); - protected final M sessionManager; - private final BiMap websockets = Maps.synchronizedBiMap(HashBiMap.create()); - - protected abstract void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt); - - protected abstract void _onOpen(WebSocket websocket, ClientHandshake handshake); - - protected abstract void _onClose(WebSocket websocket, Optional sessionOpt); - - public AbstractWebsocketServer(int port, M sessionManager) { - super(new InetSocketAddress(port), Lists.newArrayList(new Draft_6455())); - this.sessionManager = sessionManager; - } - - /** - * Open event of websocket. - */ - @Override - public final void onOpen(WebSocket websocket, ClientHandshake handshake) { - try { - this._onOpen(websocket, handshake); - } catch (Throwable e) { - log.error("onOpen-Error [" + this.handshakeToJsonObject(handshake) + "]: "); - e.printStackTrace(); - } - } - - /** - * Close event of websocket. Removes the websocket. Keeps the session. Calls - * _onClose() - */ - @Override - public final void onClose(WebSocket websocket, int code, String reason, boolean remote) { - try { - Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket)); - String sessionString; - if (sessionOpt.isPresent()) { - sessionString = sessionOpt.get().toString() + " "; - } else { - sessionString = ""; - } - log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" + reason + "]"); - this.websockets.remove(websocket); - this._onClose(websocket, sessionOpt); - } catch (Throwable e) { - log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + e.getMessage()); - } - } - - /** - * Error event of websocket. Logs the error. - */ - @Override - public final void onError(WebSocket websocket, Exception ex) { - S session = this.websockets.get(websocket); - String sessionString; - if (session == null) { - sessionString = ""; - } else { - sessionString = session.toString(); - } - log.warn(sessionString + " Websocket error: " + ex.getMessage()); - } - - /** - * Message event of websocket. Handles a new message. - */ - @Override - public final void onMessage(WebSocket websocket, String message) { - try { - JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); - Optional jMessageId = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); - Optional deviceNameOpt = JsonUtils.getAsOptionalString(jMessage, "device"); - this._onMessage(websocket, jMessage, jMessageId, deviceNameOpt); - } catch (Throwable e) { - log.error("onMessage-Error [" + message + "]: " + e.getMessage()); - e.printStackTrace(); - } - } - - /** - * Get cookie from handshake - * - * @param handshake - * @return cookie as JsonObject - */ - protected JsonObject parseCookieFromHandshake(ClientHandshake handshake) { - JsonObject j = new JsonObject(); - if (handshake.hasFieldValue("cookie")) { - String cookieString = handshake.getFieldValue("cookie"); - for (String cookieVariable : cookieString.split("; ")) { - String[] keyValue = cookieVariable.split("="); - if (keyValue.length == 2) { - j.addProperty(keyValue[0], keyValue[1]); - } - } - } - return j; - } - - /** - * Converts a Handshake to a JsonObject - * - * @param handshake - * @return - */ - protected JsonObject handshakeToJsonObject(ClientHandshake handshake) { - JsonObject j = new JsonObject(); - for (Iterator iter = handshake.iterateHttpFields(); iter.hasNext();) { - String field = iter.next(); - j.addProperty(field, handshake.getFieldValue(field)); - } - return j; - } - - @Override - public final void onStart() { - // nothing to do - } - - /** - * Returns the Websocket for the given token - * - * @param name - * @return - */ - public Optional getWebsocketByToken(String token) { - Optional sessionOpt = (Optional) this.sessionManager.getSessionByToken(token); - if (!sessionOpt.isPresent()) { - return Optional.empty(); - } - S session = sessionOpt.get(); - return Optional.ofNullable(this.websockets.inverse().get(session)); - } - - protected void addWebsocket(WebSocket websocket, S session) { - synchronized (this.websockets) { - if (this.websockets.containsKey(websocket)) { - log.warn("Websocket [" + websocket + "] already existed. Replacing existing one with session [" - + session + "]"); - } - if (this.websockets.inverse().containsKey(session)) { - log.warn("Session [" + session + "] already existed. Replacing existing one with websocket [" - + websocket + "]"); - } - this.websockets.forcePut(websocket, session); - } - } - - protected void removeWebsocket(WebSocket websocket) { - synchronized (this.websockets) { - this.websockets.remove(websocket); - } - } - - protected Optional getSessionFromWebsocket(WebSocket websocket) { - return Optional.ofNullable(this.websockets.get(websocket)); - } - - protected Optional getWebsocketFromSession(S session) { - return Optional.ofNullable(this.websockets.inverse().get(session)); - } -} diff --git a/common/src/io/openems/common/websocket/CurrentDataWorker.java b/common/src/io/openems/common/websocket/CurrentDataWorker.java deleted file mode 100644 index 941ea00b169..00000000000 --- a/common/src/io/openems/common/websocket/CurrentDataWorker.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.openems.common.websocket; - -import java.util.Optional; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import org.java_websocket.WebSocket; -import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.common.types.ChannelAddress; - -public abstract class CurrentDataWorker { - - protected final static int UPDATE_INTERVAL_IN_SECONDS = 2; - - /** - * Executor for subscriptions task - */ - private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); - - /** - * Holds thingId and channelId, subscribed by this websocket - */ - private final HashMultimap channels; - - /** - * Holds the scheduled task for currentData - */ - private final ScheduledFuture future; - - public CurrentDataWorker(JsonArray jId, Optional deviceNameOpt, HashMultimap channels) { - this.channels = channels; - this.future = this.executor.scheduleWithFixedDelay(() -> { - /* - * This task is executed regularly. Sends data to websocket. - */ - Optional wsOpt = this.getWebsocket(); - if (!(wsOpt.isPresent() && wsOpt.get().isOpen())) { - // disconnected; stop worker - this.dispose(); - return; - } - WebSocketUtils.send(wsOpt.get(), DefaultMessages.currentData(jId, deviceNameOpt, getSubscribedData())); - }, 0, UPDATE_INTERVAL_IN_SECONDS, TimeUnit.SECONDS); - } - - public void dispose() { - // unsubscribe regular task - future.cancel(true); - } - - /** - * Gets a json object with all subscribed channels - * - * @return - */ - private JsonObject getSubscribedData() { - JsonObject jData = new JsonObject(); - for (String thingId : this.channels.keys()) { - JsonObject jThingData = new JsonObject(); - for (String channelId : this.channels.get(thingId)) { - ChannelAddress channelAddress = new ChannelAddress(thingId, channelId); - Optional jValueOpt = this.getChannelValue(channelAddress); - if (jValueOpt.isPresent()) { - jThingData.add(channelId, jValueOpt.get()); - } - } - jData.add(thingId, jThingData); - } - return jData; - } - - protected abstract Optional getChannelValue(ChannelAddress channelAddress); - - protected abstract Optional getWebsocket(); -} diff --git a/common/src/io/openems/common/websocket/DefaultMessages.java b/common/src/io/openems/common/websocket/DefaultMessages.java deleted file mode 100644 index 85db6caef14..00000000000 --- a/common/src/io/openems/common/websocket/DefaultMessages.java +++ /dev/null @@ -1,357 +0,0 @@ -package io.openems.common.websocket; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map.Entry; -import java.util.Optional; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openems.common.types.ChannelAddress; -import io.openems.common.types.DeviceImpl; -import io.openems.common.types.FieldValue; -import io.openems.common.types.NumberFieldValue; -import io.openems.common.types.StringFieldValue; - -public class DefaultMessages { - - /** - *
        -	 *	{
        -	 *		authenticate: {
        -	 *			mode: "allow",
        -	 *			token: String
        -	 *		}, metadata: {
        -	 *			devices: [{
        -	 *				name: String,
        -	 *				comment: String,
        -	 *				producttype: String,
        -	 *				role: "admin" | "installer" | "owner" | "guest",
        -	 *				online: boolean
        -	 *			}]
        -	 *		}
        -	 *	}
        -	 * - authenticate.role is only sent for OpenEMS Edge
        -	 * - metadata.devices is only sent for OpenEMS Backend
        -	 * 
        - * - * @param token - * @return - */ - public static JsonObject browserConnectionSuccessfulReply(String token, Optional roleOpt, - Collection devices) { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "allow"); - if (roleOpt.isPresent()) { - jAuthenticate.addProperty("role", roleOpt.get()); - } - jAuthenticate.addProperty("token", token); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - JsonObject jMetadata = new JsonObject(); - if (!devices.isEmpty()) { - JsonArray jDevices = new JsonArray(); - for (DeviceImpl device : devices) { - JsonObject jDevice = new JsonObject(); - jDevice.addProperty("name", device.getName()); - jDevice.addProperty("comment", device.getComment()); - jDevice.addProperty("producttype", device.getProducttype()); - jDevice.addProperty("role", device.getRole().toString()); - jDevice.addProperty("online", device.isOnline()); - jDevices.add(jDevice); - } - jMetadata.add("devices", jDevices); - } - j.add("metadata", jMetadata); - return j; - } - - /** - *
        -	 *	{
        -	 *		authenticate: {
        -	 *			mode: "deny"
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @param token - * @return - */ - public static JsonObject browserConnectionFailedReply() { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "deny"); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - return j; - } - - /** - *
        -	 *	{
        -	 *		authenticate: {
        -	 *			mode: "allow"
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @param token - * @return - */ - public static JsonObject openemsConnectionSuccessfulReply() { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "allow"); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - return j; - } - - /** - *
        -	 *	{
        -	 *		authenticate: {
        -	 *			mode: "deny",
        -	 *			message: String
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @param token - * @return - */ - public static JsonObject openemsConnectionFailedReply(String message) { - JsonObject jAuthenticate = new JsonObject(); - jAuthenticate.addProperty("mode", "deny"); - jAuthenticate.addProperty("message", message); - JsonObject j = new JsonObject(); - j.add("authenticate", jAuthenticate); - return j; - } - - /** - *
        -	 *	{
        -	 *		timedata: {
        -	 *			timestamp (Long): {
        -	 *				channel: String,
        -	 *				value: String | Number
        -	 *			}
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @param token - * @return - */ - public static JsonObject timestampedData(long timestamp, HashMap> queue) { - JsonObject jTimestamp = new JsonObject(); - for (Entry> entry : queue.entrySet()) { - String address = entry.getKey().toString(); - FieldValue fieldValue = entry.getValue(); - if (fieldValue instanceof NumberFieldValue) { - jTimestamp.addProperty(address, ((NumberFieldValue) fieldValue).value); - } else if (fieldValue instanceof StringFieldValue) { - jTimestamp.addProperty(address, ((StringFieldValue) fieldValue).value); - } - } - JsonObject jTimedata = new JsonObject(); - jTimedata.add(String.valueOf(timestamp), jTimestamp); - JsonObject j = new JsonObject(); - j.add("timedata", jTimedata); - return j; - } - - /** - *
        -	 *	{
        -	 *		config: {
        -	 *			...
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @param token - * @return - */ - public static JsonObject configQueryReply(JsonObject config) { - JsonObject j = new JsonObject(); - j.add("config", config); - return j; - } - - /** - *
        -	 *	{
        -	 *		id: [string],
        -	 *		currentData: {[{ 
        -	 *			channel: string,
        -	 *			value: any
        -	 *		}]}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject currentData(JsonArray jId, Optional deviceNameOpt, JsonObject jCurrentData) { - JsonObject j = new JsonObject(); - j.add("id", jId); - if(deviceNameOpt.isPresent()) { - j.addProperty("device", deviceNameOpt.get()); - } - j.add("currentData", jCurrentData); - return j; - } - - /** - *
        -	 *	{
        -	 *		id: [string]
        -	 *		historicData: {
        -	 *			data: [{
        -	 *				time: ...,
        -	 *				channels: {
        -	 *					thing: {
        -	 *						channel: any
        -	 *					} 
        -	 *				}
        -	 *			}]
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject historicDataQueryReply(JsonArray jId, JsonArray jData) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jHistoricData = new JsonObject(); - jHistoricData.add("data", jData); - j.add("historicData", jHistoricData); - return j; - } - - /** - *
        -	 *	{
        -	 *		notification: {
        -	 *			id: string[],
        -	 *			type: string,
        -	 *			message: string,
        -	 *			code: number,
        -	 *			params: string[]
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject notification(JsonArray jId, Notification code, String message, Object... params) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jNotification = new JsonObject(); - jNotification.addProperty("type", code.getType().toString().toLowerCase()); - jNotification.addProperty("message", message); - jNotification.addProperty("code", code.getValue()); - JsonArray jParams = new JsonArray(); - for (Object param : params) { - jParams.add(param.toString()); - } - jNotification.add("params", jParams); - j.add("notification", jNotification); - return j; - } - - /** - *
        -	 *	{
        -	 *		currentData: {
        -	 *			mode: 'subscribe',
        -	 *			channels: {}
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannels) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jCurrentData = new JsonObject(); - jCurrentData.addProperty("mode", "subscribe"); - jCurrentData.add("channels", jChannels); - j.add("currentData", jCurrentData); - return j; - } - - /** - *
        -	 *	{
        -	 *		id: [string],
        -	 *		log: {
        -	 *			times: number,
        -	 *			level: string,
        -	 *			source: string,
        -	 *			message: string
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject log(JsonArray jId, long timestamp, String level, String source, String message) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jLog = new JsonObject(); - jLog.addProperty("time", timestamp); - jLog.addProperty("level", level); - jLog.addProperty("source", source); - jLog.addProperty("message", message); - j.add("log", jLog); - return j; - } - - /** - *
        -	 *	{
        -	 *		id: [string],
        -	 *		log: {
        -	 *			mode: "unsubscribe"
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject logUnsubscribe(JsonArray jId) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jLog = new JsonObject(); - jLog.addProperty("mode", "unsubscribe"); - j.add("log", jLog); - return j; - } - - /** - *
        -	 *	{
        -	 *		id: [string],
        -	 *		system: {
        -	 *			mode: "executeReply",
        -	 *			output: string
        -	 *		}
        -	 *	}
        -	 * 
        - * - * @return - */ - public static JsonObject systemExecuteReply(JsonArray jId, String output) { - JsonObject j = new JsonObject(); - j.add("id", jId); - JsonObject jSystem = new JsonObject(); - jSystem.addProperty("mode", "executeReply"); - jSystem.addProperty("output", output); - j.add("system", jSystem); - return j; - } -} diff --git a/common/src/io/openems/common/websocket/LogBehaviour.java b/common/src/io/openems/common/websocket/LogBehaviour.java deleted file mode 100644 index c74395a7b5c..00000000000 --- a/common/src/io/openems/common/websocket/LogBehaviour.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.openems.common.websocket; - -public enum LogBehaviour { - WRITE_TO_LOG, DO_NOT_WRITE_TO_LOG; -} diff --git a/common/src/io/openems/common/websocket/Notification.java b/common/src/io/openems/common/websocket/Notification.java deleted file mode 100644 index fb3f72b4270..00000000000 --- a/common/src/io/openems/common/websocket/Notification.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.openems.common.websocket; - -import org.slf4j.Logger; - -public enum Notification { - EDGE_CONNECTION_ClOSED(100, NotificationType.WARNING, "Connection [%s] was interrupted"), - EDGE_CONNECTION_OPENED(101, NotificationType.INFO, "Connection [%s] was established"), - EDGE_UNABLE_TO_FORWARD(102, NotificationType.ERROR, "Unable to forward command to [%s]: %s"), - EDGE_AUTHENTICATION_BY_TOKEN_FAILED(103, NotificationType.INFO, "Authentication by token [%s] failed"), - EDGE_CHANNEL_UPDATE_SUCCESS(104, NotificationType.SUCCESS, "Configuration successfully updated [%s]"), - EDGE_CHANNEL_UPDATE_FAILED(105, NotificationType.ERROR, "Configuration update failed [%s]: %s"), - BACKEND_NOT_ALLOWED(106, NotificationType.ERROR, "The operation [%s] is not allowed via Backend"), - EDGE_CHANNEL_UPDATE_TIMEOUT(107, NotificationType.INFO, "Channel setting [%s] timed out."); - - private final int value; - private final NotificationType status; - private final String message; - - private Notification(int value, NotificationType status, String message) { - this.value = value; - this.status = status; - this.message = message; - } - - public int getValue() { - return value; - } - - public NotificationType getType() { - return status; - } - - public String getMessage() { - return message; - } - - public void writeToLog(Logger log, Object... params) { - String message = String.format(this.message, params); - String logMessage = "Notification [" + this.value + "]: " + message; - switch (this.status) { - case INFO: - case LOG: - case SUCCESS: - log.info(logMessage); - break; - case ERROR: - log.error(logMessage); - break; - case WARNING: - log.warn(logMessage); - break; - } - } -} diff --git a/common/src/io/openems/common/websocket/NotificationType.java b/common/src/io/openems/common/websocket/NotificationType.java deleted file mode 100644 index 4d087b1e5b3..00000000000 --- a/common/src/io/openems/common/websocket/NotificationType.java +++ /dev/null @@ -1,25 +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.common.websocket; - -public enum NotificationType { - SUCCESS, ERROR, WARNING, INFO, LOG; -} diff --git a/common/src/io/openems/common/websocket/WebSocketUtils.java b/common/src/io/openems/common/websocket/WebSocketUtils.java deleted file mode 100644 index 7072b091f95..00000000000 --- a/common/src/io/openems/common/websocket/WebSocketUtils.java +++ /dev/null @@ -1,113 +0,0 @@ -package io.openems.common.websocket; - -import java.time.Period; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.java_websocket.exceptions.WebsocketNotConnectedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openems.common.api.TimedataSource; -import io.openems.common.session.Role; -import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.StringUtils; - -public class WebSocketUtils { - - private static Logger log = LoggerFactory.getLogger(WebSocketUtils.class); - - public static boolean send(Optional websocketOpt, JsonObject j) { - if (!websocketOpt.isPresent()) { - log.error("Websocket is not available. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); - return false; - } else { - return WebSocketUtils.send(websocketOpt.get(), j); - } - } - - public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour, Notification code, - Object... params) { - if (!websocketOpt.isPresent()) { - log.error("Websocket is not available. Unable to send Notification [" - + String.format(code.getMessage(), params) + "]"); - return false; - } else { - return WebSocketUtils.sendNotification(websocketOpt.get(), jId, logBehaviour, code, params); - } - } - - public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour, Notification notification, Object... params) { - if (logBehaviour.equals(LogBehaviour.WRITE_TO_LOG)) { - // log message - notification.writeToLog(log, params); - } - String message = String.format(notification.getMessage(), params); - JsonObject j = DefaultMessages.notification(jId, notification, message, params); - return WebSocketUtils.send(websocket, j); - } - - /** - * Send a message to a websocket - * - * @param j - * @return true if successful, otherwise false - */ - public static boolean send(WebSocket websocket, JsonObject j) { - // System.out.println("SEND: websocket["+websocket+"]: " + j.toString()); - try { - websocket.send(j.toString()); - return true; - } catch (WebsocketNotConnectedException e) { - log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]"); - return false; - } - } - - /** - * Query history command - * - * @param j - */ - public static JsonObject historicData(JsonArray jMessageId, JsonObject jHistoricData, Optional deviceId, - TimedataSource timedataSource, Role role) { - try { - String mode = JsonUtils.getAsString(jHistoricData, "mode"); - if (mode.equals("query")) { - /* - * Query historic data - */ - int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); - ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); - ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); - ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); - JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); - // TODO check if role is allowed to read these channels - // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); - int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); - // TODO: better calculation of sensible resolution - int resolution = 10 * 60; // 10 Minutes - if (days > 25) { - resolution = 24 * 60 * 60; // 1 Day - } else if (days > 6) { - resolution = 3 * 60 * 60; // 3 Hours - } else if (days > 2) { - resolution = 60 * 60; // 60 Minutes - } - JsonArray jData = timedataSource.queryHistoricData(deviceId, fromDate, toDate, channels, resolution); - // send reply - return DefaultMessages.historicDataQueryReply(jMessageId, jData); - } - } catch (Exception e) { - log.error("HistoricData Error: ", e); - e.printStackTrace(); - } - return new JsonObject(); - } -} diff --git a/edge/.classpath b/edge/.classpath index 77874fed6c6..2560febd213 100644 --- a/edge/.classpath +++ b/edge/.classpath @@ -1,18 +1,20 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/edge/pom.xml b/edge/pom.xml index af920677169..3c4f8cb119a 100644 --- a/edge/pom.xml +++ b/edge/pom.xml @@ -36,11 +36,6 @@ - - io.openems - common - ${project.version} - com.google.code.gson gson diff --git a/edge/src/io/openems/api/doc/ChannelInfo.java b/edge/src/io/openems/api/doc/ChannelInfo.java index e6c4d31c5a1..3372467d049 100644 --- a/edge/src/io/openems/api/doc/ChannelInfo.java +++ b/edge/src/io/openems/api/doc/ChannelInfo.java @@ -1,77 +1,77 @@ -/******************************************************************************* - * 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.api.doc; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.util.Set; - -import com.google.common.collect.Sets; - -import io.openems.common.session.Role; - -@Retention(RUNTIME) -@Target({ FIELD, METHOD }) -public @interface ChannelInfo { - - public static final String DEFAULT_DESCRIPTION = ""; - public static final String DEFAULT_TITLE = ""; - public static final boolean DEFAULT_IS_OPTIONAL = false; - public static final boolean DEFAULT_IS_ARRAY = false; - public static final Set DEFAULT_READ_ROLES = Sets.newHashSet(Role.GUEST, Role.OWNER, Role.INSTALLER, - Role.ADMIN); - public static final Set DEFAULT_WRITE_ROLES = Sets.newHashSet(Role.ADMIN); - public static final String DEFAULT_VALUE = ""; - - String title() default DEFAULT_TITLE; - - String description() default DEFAULT_DESCRIPTION; - - Class type(); - - boolean isOptional() default DEFAULT_IS_OPTIONAL; - - boolean isArray() default DEFAULT_IS_ARRAY; - - /** - * By default all roles are allowed to read. ADMIN is added automatically to this list. - * - * @return - */ - Role[] readRoles() default { Role.GUEST, Role.OWNER, Role.INSTALLER, Role.ADMIN }; - /** - * By default only the "ADMIN" role is allowed to write. ADMIN is added automatically to this list. - * - * @return - */ - Role[] writeRoles() default { Role.ADMIN }; - - /** - * String is interpreted as a JsonElement - * - * @return - */ - String defaultValue() default DEFAULT_VALUE; -} +/******************************************************************************* + * 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.api.doc; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Set; + +import com.google.common.collect.Sets; + +import io.openems.common.session.Role; + +@Retention(RUNTIME) +@Target({ FIELD, METHOD }) +public @interface ChannelInfo { + + public static final String DEFAULT_DESCRIPTION = ""; + public static final String DEFAULT_TITLE = ""; + public static final boolean DEFAULT_IS_OPTIONAL = false; + public static final boolean DEFAULT_IS_ARRAY = false; + public static final Set DEFAULT_READ_ROLES = Sets.newHashSet(Role.GUEST, Role.OWNER, Role.INSTALLER, + Role.ADMIN); + public static final Set DEFAULT_WRITE_ROLES = Sets.newHashSet(Role.ADMIN); + public static final String DEFAULT_VALUE = ""; + + String title() default DEFAULT_TITLE; + + String description() default DEFAULT_DESCRIPTION; + + Class type(); + + boolean isOptional() default DEFAULT_IS_OPTIONAL; + + boolean isArray() default DEFAULT_IS_ARRAY; + + /** + * By default all roles are allowed to read. ADMIN is added automatically to this list. + * + * @return + */ + Role[] readRoles() default { Role.GUEST, Role.OWNER, Role.INSTALLER, Role.ADMIN }; + /** + * By default only the "ADMIN" role is allowed to write. ADMIN is added automatically to this list. + * + * @return + */ + Role[] writeRoles() default { Role.ADMIN }; + + /** + * String is interpreted as a JsonElement + * + * @return + */ + String defaultValue() default DEFAULT_VALUE; +} diff --git a/edge/src/io/openems/api/persistence/QueryablePersistence.java b/edge/src/io/openems/api/persistence/QueryablePersistence.java index a40fc44ef33..d9e12096bb3 100644 --- a/edge/src/io/openems/api/persistence/QueryablePersistence.java +++ b/edge/src/io/openems/api/persistence/QueryablePersistence.java @@ -1,27 +1,42 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.api.persistence; - -import io.openems.common.api.TimedataSource; - -public abstract class QueryablePersistence extends Persistence implements TimedataSource { - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.api.persistence; + +import java.util.Optional; + +import com.google.gson.JsonObject; + +import io.openems.backend.timedata.api.TimedataService; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; + +public abstract class QueryablePersistence extends Persistence implements TimedataService { + @Override + public Optional getChannelValue(int edgeId, ChannelAddress channelAddress) { + // TODO implement after migration to OSGi + return null; + } + + @Override + public void write(int edgeId, JsonObject jData) throws OpenemsException { + // TODO implement after migration to OSGi + } +} diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java index 682fd657043..8560c523e5b 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java @@ -2,11 +2,9 @@ import java.util.Optional; -import org.java_websocket.WebSocket; - import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.openems.api.channel.Channel; import io.openems.common.exceptions.AccessDeniedException; @@ -29,9 +27,10 @@ public class EdgeCurrentDataWorker extends CurrentDataWorker { */ private final Role role; - public EdgeCurrentDataWorker(JsonArray jId, HashMultimap channels, Role role, + public EdgeCurrentDataWorker(JsonObject jMessageId, HashMultimap channels, Role role, EdgeWebsocketHandler edgeWebsocketHandler) { - super(jId, Optional.empty(), channels); + // TODO make sure websocket is present + super(edgeWebsocketHandler.getWebsocket().get(), jMessageId, channels); this.role = role; this.edgeWebsocketHandler = edgeWebsocketHandler; this.thingRepository = ThingRepository.getInstance(); @@ -57,9 +56,4 @@ protected Optional getChannelValue(ChannelAddress channelAddress) { // TODO log error message: channel not found } } - - @Override - protected Optional getWebsocket() { - return this.edgeWebsocketHandler.getWebsocket(); - } } diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java index 3f5c392e5f6..ea045e53ab9 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java @@ -1,672 +1,677 @@ -/******************************************************************************* - * 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.core.utilities.websocket; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.java_websocket.WebSocket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.HashMultimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.persistence.QueryablePersistence; -import io.openems.common.api.TimedataSource; -import io.openems.common.exceptions.AccessDeniedException; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.session.Role; -import io.openems.common.utils.JsonUtils; -import io.openems.common.websocket.CurrentDataWorker; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.LogBehaviour; -import io.openems.common.websocket.Notification; -import io.openems.common.websocket.WebSocketUtils; -import io.openems.core.Config; -import io.openems.core.ConfigFormat; -import io.openems.core.ThingRepository; -import io.openems.core.utilities.ConfigUtils; -import io.openems.core.utilities.LinuxCommand; -import io.openems.core.utilities.api.ApiWorker; -import io.openems.core.utilities.api.WriteJsonObject; -import io.openems.core.utilities.api.WriteObject; - -/** - * Handles a Websocket connection to a browser, OpenEMS backend,... - * - * @author stefan.feilmeier - */ -public class EdgeWebsocketHandler { - - private Logger log = LoggerFactory.getLogger(EdgeWebsocketHandler.class); - - /** - * Holds the websocket connection - */ - protected Optional websocketOpt = Optional.empty(); - - /** - * Holds subscribers to current data - */ - private final HashMap currentDataSubscribers = new HashMap<>(); - - /** - * Holds subscribers to system log - */ - private final Set logSubscribers = new HashSet<>(); - - /** - * Executor for system log task - */ - private final ExecutorService logExecutor = Executors.newCachedThreadPool(); - - /** - * Predefined role for this connection. If empty, role is taken from message (in onMessage method). - */ - private final Optional roleOpt; - - /** - * ApiWorker handles updates to WriteChannels - */ - private final Optional apiWorkerOpt; - - public EdgeWebsocketHandler() { - this.apiWorkerOpt = Optional.empty(); - this.roleOpt = Optional.empty(); - } - - public EdgeWebsocketHandler(ApiWorker apiWorker, WebSocket websocket, Role role) { - this.apiWorkerOpt = Optional.ofNullable(apiWorker); - this.websocketOpt = Optional.ofNullable(websocket); - this.roleOpt = Optional.ofNullable(role); - } - - public void setWebsocket(WebSocket websocket) { - this.websocketOpt = Optional.ofNullable(websocket); - } - - public Optional getWebsocket() { - return websocketOpt; - } - - /** - * Handles a message from Websocket. - * - * @param jMessage - */ - public final void onMessage(JsonObject jMessage) { - // get message id -> used for reply - Optional jIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); - - // get role - Role role; - if (this.roleOpt.isPresent()) { - role = this.roleOpt.get(); - } else { - Optional roleStringOpt = JsonUtils.getAsOptionalString(jMessage, "role"); - if (roleStringOpt.isPresent()) { - role = Role.getRole(roleStringOpt.get()); - } else { - role = Role.getDefaultRole(); - } - } - - // prepare reply (every reply is going to be merged into this object with this unique message id) - JsonObject jReply = new JsonObject(); - - // init deviceId as empty. It's only needed in backend - Optional deviceIdOpt = Optional.empty(); - - /* - * Config - */ - Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); - if (jConfigOpt.isPresent()) { - jReply = JsonUtils.merge(jReply, // - config(jConfigOpt.get(), role, jIdOpt.orElse(new JsonArray()), apiWorkerOpt) // - ); - } - - /* - * Subscribe to currentData - */ - Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); - if (jCurrentDataOpt.isPresent() && jIdOpt.isPresent()) { - jReply = JsonUtils.merge(jReply, // - currentData(jIdOpt.get(), jCurrentDataOpt.get(), role) // - ); - } - - /* - * Query historic data - */ - // Optional jMessageIdOpt, String deviceName, WebSocket websocket, JsonElement jHistoricDataElement - Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); - if (jhistoricDataOpt.isPresent() && jIdOpt.isPresent()) { - // select first QueryablePersistence (by default the running InfluxdbPersistence) - TimedataSource timedataSource = null; - for (QueryablePersistence queryablePersistence : ThingRepository.getInstance().getQueryablePersistences()) { - timedataSource = queryablePersistence; - break; - } - if (timedataSource == null) { - // TODO create notification that there is no datasource available - } else { - jReply = JsonUtils.merge(jReply, // - WebSocketUtils.historicData(jIdOpt.get(), jhistoricDataOpt.get(), deviceIdOpt, timedataSource, - role) // - ); - } - } - - /* - * Subscribe to log - */ - Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log"); - if (jLogOpt.isPresent() && jIdOpt.isPresent()) { - try { - jReply = JsonUtils.merge(jReply, // - log(jIdOpt.get(), jLogOpt.get(), role) // - ); - } catch (AccessDeniedException e) { /* ignore */ } - } - - /* - * Remote system control - */ - { - Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system"); - if (jSystemOpt.isPresent() && jSystemOpt.isPresent()) { - try { - jReply = JsonUtils.merge(jReply, // - system(jIdOpt.get(), jSystemOpt.get(), role) // - ); - } catch (AccessDeniedException e) { - // TODO create notification - log.error(e.getMessage()); - } - } - } - - // send reply - if (jReply.entrySet().size() > 0) { - if (jIdOpt.isPresent()) { - jReply.add("id", jIdOpt.get()); - } - WebSocketUtils.send(this.websocketOpt, jReply); - } - } - - /** - * Handle "config" messages - * - * @param jConfig - * @return - */ - private synchronized JsonObject config(JsonObject jConfig, Role role, JsonArray jId, Optional apiWorkerOpt) { - try { - String mode = JsonUtils.getAsString(jConfig, "mode"); - - if (mode.equals("query")) { - /* - * Query current config - */ - String language = JsonUtils.getAsString(jConfig, "language"); - JsonObject jReplyConfig = Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, role, language); - return DefaultMessages.configQueryReply(jReplyConfig); - - } else if (mode.equals("update")) { - try { - /* - * Update thing/channel config - */ - String thingId = JsonUtils.getAsString(jConfig, "thing"); - String channelId = JsonUtils.getAsString(jConfig, "channel"); - JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); - Optional channelOpt = ThingRepository.getInstance().getChannel(thingId, channelId); - if (channelOpt.isPresent()) { - Channel channel = channelOpt.get(); - // check write permissions - channel.assertWriteAllowed(role); - if (channel instanceof ConfigChannel) { - /* - * ConfigChannel - */ - ConfigChannel configChannel = (ConfigChannel) channel; - Object value = ConfigUtils.getConfigObject(configChannel, jValue); - configChannel.updateValue(value, true); - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue); - - } else if (channel instanceof WriteChannel) { - /* - * WriteChannel - */ - WriteChannel writeChannel = (WriteChannel) channel; - if(!apiWorkerOpt.isPresent()) { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.BACKEND_NOT_ALLOWED, "set " + channel.address() + " => " + jValue); - } else { - ApiWorker apiWorker = apiWorkerOpt.get(); - WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_SUCCESS, "set " + channel.address() + " => " + jValue); - }).onFirstError((e) -> { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_FAILED, "set " + channel.address() + " => " + jValue, e.getMessage()); - }).onTimeout(() -> { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, "set " + channel.address() + " => " + jValue); - }); - apiWorker.addValue(writeChannel, writeObject); - } - } - } else { - throw new OpenemsException("Unable to find Channel [" + thingId + "/" + channelId + "]"); - } - } catch (OpenemsException e) { - WebSocketUtils.send(websocketOpt, - DefaultMessages.notification(jId, Notification.EDGE_CHANNEL_UPDATE_FAILED, e.getMessage())); - } - } - } catch (OpenemsException e) { - log.warn(e.getMessage()); - } - return new JsonObject(); - } - - /** - * Handle current data subscriptions - * (try to keep synced with Backend.BrowserWebsocket) - * - * @param j - */ - private synchronized JsonObject currentData(JsonArray jId, JsonObject jCurrentData, Role role) { - try { - String mode = JsonUtils.getAsString(jCurrentData, "mode"); - - if (mode.equals("subscribe")) { - /* - * Subscribe to channels - */ - String messageId = jId.get(jId.size() - 1).getAsString(); - - // remove old worker if existed - CurrentDataWorker worker = this.currentDataSubscribers.remove(messageId); - if (worker != null) { - worker.dispose(); - } - // parse subscribed channels - HashMultimap channels = HashMultimap.create(); - JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); - for (Entry entry : jSubscribeChannels.entrySet()) { - String thing = entry.getKey(); - JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement jChannel : jChannels) { - String channel = JsonUtils.getAsString(jChannel); - channels.put(thing, channel); - } - } - if (!channels.isEmpty()) { - // create new worker - worker = new EdgeCurrentDataWorker(jId, channels, role, this); - this.currentDataSubscribers.put(messageId, worker); - } - } - } catch (OpenemsException e) { - log.warn(e.getMessage()); - } - return new JsonObject(); - } - - /** - * Handle system log subscriptions - * - * @param j - * @throws AccessDeniedException - */ - private synchronized JsonObject log(JsonArray jId, JsonObject jLog, Role role) throws AccessDeniedException { - if (!(role == Role.ADMIN || role == Role.INSTALLER || role == Role.OWNER)) { - throw new AccessDeniedException("User role [" + role + "] is not allowed to read system logs."); - } - try { - String mode = JsonUtils.getAsString(jLog, "mode"); - String messageId = jId.get(jId.size() - 1).getAsString(); - - if (mode.equals("subscribe")) { - /* - * Subscribe to system log - */ - this.logSubscribers.add(messageId); - } else if (mode.equals("unsubscribe")) { - /* - * Unsubscribe from system log - */ - this.logSubscribers.remove(messageId); - } - } catch (OpenemsException e) { - log.warn(e.getMessage()); - } - return new JsonObject(); - } - - /** - * Handle remote system control - * - * @param j - * @throws AccessDeniedException - */ - private synchronized JsonObject system(JsonArray jId, JsonObject jSystem, Role role) throws AccessDeniedException { - if (!(role == Role.ADMIN)) { - throw new AccessDeniedException("User role [" + role + "] is not allowed to execute system commands."); - } - String output = ""; - try { - String mode = JsonUtils.getAsString(jSystem, "mode"); - String password = JsonUtils.getAsString(jSystem, "password"); - String command = JsonUtils.getAsString(jSystem, "command"); - boolean background = JsonUtils.getAsBoolean(jSystem, "background"); - int timeout = JsonUtils.getAsInt(jSystem, "timeout"); - - if (mode.equals("execute")) { - output = LinuxCommand.execute(password, command, background, timeout); - } - } catch (OpenemsException e) { - output += e.getMessage(); - } - return DefaultMessages.systemExecuteReply(jId, output); - } - - // TODO handle config command - // /** - // * Set configuration - // * - // * @param j - // */ - // private synchronized void configure(JsonElement jConfigsElement) { - // try { - // JsonArray jConfigs = JsonUtils.getAsJsonArray(jConfigsElement); - // ThingRepository thingRepository = ThingRepository.getInstance(); - // for (JsonElement jConfigElement : jConfigs) { - // JsonObject jConfig = JsonUtils.getAsJsonObject(jConfigElement); - // String mode = JsonUtils.getAsString(jConfig, "mode"); - // if (mode.equals("update")) { - // /* - // * Channel Set mode - // */ - // String thingId = JsonUtils.getAsString(jConfig, "thing"); - // String channelId = JsonUtils.getAsString(jConfig, "channel"); - // JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); - // Optional channelOptional = thingRepository.getChannel(thingId, channelId); - // if (channelOptional.isPresent()) { - // Channel channel = channelOptional.get(); - // if (channel instanceof ConfigChannel) { - // /* - // * ConfigChannel - // */ - // ConfigChannel configChannel = (ConfigChannel) channel; - // configChannel.updateValue(jValue, true); - // Notification.send(NotificationType.SUCCESS, - // "Successfully updated [" + channel.address() + "] to [" + jValue + "]"); - // - // } else if (channel instanceof WriteChannel) { - // /* - // * WriteChannel - // */ - // WriteChannel writeChannel = (WriteChannel) channel; - // writeChannel.pushWrite(jValue); - // Notification.send(NotificationType.SUCCESS, - // "Successfully set [" + channel.address() + "] to [" + jValue + "]"); - // } - // } else { - // throw new ConfigException("Unable to find " + jConfig.toString()); - // } - // } else if (mode.equals("create")) { - // /* - // * Create new Thing - // */ - // JsonObject jObject = JsonUtils.getAsJsonObject(jConfig, "object"); - // String parentId = JsonUtils.getAsString(jConfig, "parent"); - // String thingId = JsonUtils.getAsString(jObject, "id"); - // if (thingId.startsWith("_")) { - // throw new ConfigException("IDs starting with underscore are reserved for internal use."); - // } - // if (thingRepository.getThingById(thingId).isPresent()) { - // throw new ConfigException("Thing Id is already existing."); - // } - // String clazzName = JsonUtils.getAsString(jObject, "class"); - // Class clazz = Class.forName(clazzName); - // if (Device.class.isAssignableFrom(clazz)) { - // // Device - // Thing parentThing = thingRepository.getThing(parentId); - // if (parentThing instanceof Bridge) { - // Bridge parentBridge = (Bridge) parentThing; - // Device device = thingRepository.createDevice(jObject); - // parentBridge.addDevice(device); - // Config.getInstance().writeConfigFile(); - // Notification.send(NotificationType.SUCCESS, "Device [" + device.id() + "] wurde erstellt."); - // break; - // } - // } - // } else if (mode.equals("delete")) { - // /* - // * Delete a Thing - // */ - // String thingId = JsonUtils.getAsString(jConfig, "thing"); - // thingRepository.removeThing(thingId); - // Config.getInstance().writeConfigFile(); - // Notification.send(NotificationType.SUCCESS, "Controller [" + thingId + "] wurde " + " gel�scht."); - // } else { - // throw new OpenemsException("Modus [" + mode + "] ist nicht implementiert."); - // } - // } - // // Send new config - // JsonObject jMetadata = new JsonObject(); - // // TODO jMetadata.add("config", Config.getInstance().getMetaConfigJson()); - // JsonObject j = new JsonObject(); - // j.add("metadata", jMetadata); - // WebSocketUtils.send(this.websocket, j); - // } catch (OpenemsException | ClassNotFoundException e) { - // Notification.send(NotificationType.ERROR, e.getMessage()); - // } - // } - - // TODO handle system command - // /** - // * System command - // * - // * @param j - // */ - // private synchronized void system(JsonElement jSystemElement) { - // JsonObject jNotification = new JsonObject(); - // try { - // JsonObject jSystem = JsonUtils.getAsJsonObject(jSystemElement); - // String mode = JsonUtils.getAsString(jSystem, "mode"); - // if (mode.equals("systemd-restart")) { - // /* - // * Restart systemd service - // */ - // String service = JsonUtils.getAsString(jSystem, "service"); - // if (service.equals("fems-pagekite")) { - // ProcessBuilder builder = new ProcessBuilder("/bin/systemctl", "restart", "fems-pagekite"); - // Process p = builder.start(); - // if (p.waitFor() == 0) { - // log.info("Successfully restarted fems-pagekite"); - // } else { - // throw new OpenemsException("restart fems-pagekite failed"); - // } - // } else { - // throw new OpenemsException("Unknown systemd-restart service: " + jSystemElement.toString()); - // } - // - // } else if (mode.equals("manualpq")) { - // /* - // * Manual PQ settings - // */ - // String ess = JsonUtils.getAsString(jSystem, "ess"); - // Boolean active = JsonUtils.getAsBoolean(jSystem, "active"); - // if (active) { - // Long p = JsonUtils.getAsLong(jSystem, "p"); - // Long q = JsonUtils.getAsLong(jSystem, "q"); - // if (this.controller == null) { - // throw new OpenemsException("Local access only. Controller is null."); - // } - // this.controller.setManualPQ(ess, p, q); - // Notification.send(NotificationType.SUCCESS, - // "Leistungsvorgabe gesetzt: ess[" + ess + "], p[" + p + "], q[" + q + "]"); - // } else { - // this.controller.resetManualPQ(ess); - // Notification.send(NotificationType.SUCCESS, "Leistungsvorgabe gestoppt: ess[" + ess + "]"); - // } - // } else { - // throw new OpenemsException("Unknown system message: " + jSystemElement.toString()); - // } - // } catch (OpenemsException | IOException | InterruptedException e) { - // Notification.send(NotificationType.ERROR, e.getMessage()); - // } - // } - - // TODO handle manual PQ - // private void manualPQ(JsonElement j, AuthenticatedWebsocketHandler handler) { - // try { - // JsonObject jPQ = JsonUtils.getAsJsonObject(j); - // if (jPQ.has("p") && jPQ.has("q")) { - // long p = JsonUtils.getAsLong(jPQ, "p"); - // long q = JsonUtils.getAsLong(jPQ, "q"); - // this.controller.setManualPQ(p, q); - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabe gesetzt: P=" + p + ",Q=" + q); - // } else { - // // stop manual PQ - // this.controller.resetManualPQ(); - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabe zurückgesetzt"); - // } - // } catch (ReflectionException e) { - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabewerte falsch: " + e.getMessage()); - // } - // } - - // TODO handle channel commands - // private void channel(JsonElement jChannelElement, AuthenticatedWebsocketHandler handler) { - // try { - // JsonObject jChannel = JsonUtils.getAsJsonObject(jChannelElement); - // String thingId = JsonUtils.getAsString(jChannel, "thing"); - // String channelId = JsonUtils.getAsString(jChannel, "channel"); - // JsonElement jValue = JsonUtils.getSubElement(jChannel, "value"); - // - // // get channel - // Channel channel; - // Optional channelOptional = thingRepository.getChannel(thingId, channelId); - // if (channelOptional.isPresent()) { - // // get channel value - // channel = channelOptional.get(); - // } else { - // // Channel not found - // throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); - // } - // - // // check for writable channel - // if (!(channel instanceof WriteChannel)) { - // throw new ResourceException(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - // } - // - // // set channel value - // if (channel instanceof ConfigChannel) { - // // is a ConfigChannel - // ConfigChannel configChannel = (ConfigChannel) channel; - // try { - // configChannel.updateValue(jValue, true); - // log.info("Updated Channel [" + channel.address() + "] to value [" + jValue.toString() + "]."); - // handler.sendNotification(NotificationType.SUCCESS, - // "Channel [" + channel.address() + "] aktualisiert zu [" + jValue.toString() + "]."); - // } catch (NotImplementedException e) { - // throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Conversion not implemented"); - // } - // } else { - // // is a WriteChannel - // handler.sendNotification(NotificationType.WARNING, "WriteChannel nicht implementiert"); - // } - // } catch (ReflectionException e) { - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabewerte falsch: " + e.getMessage()); - // } - // } - - /** - * Send a notification message/error to the websocket - * - * @param mesage - * @return true if successful, otherwise false - */ - // TODO send notification - // public synchronized void sendNotification(NotificationType type, String message) { - // JsonObject jNotification = new JsonObject(); - // jNotification.addProperty("type", type.name().toLowerCase()); - // jNotification.addProperty("message", message); - // JsonObject j = new JsonObject(); - // j.add("notification", jNotification); - // new Thread(() -> { - // WebSocketUtils.send(websocket, j); - // }).start(); - // } - - /** - * Send a message to the websocket. - * - * @param message - */ - public boolean send(JsonObject j) { - return WebSocketUtils.send(this.websocketOpt, j); - } - - /** - * Send a log message to the websocket. This method is called by logback - * - * @param message2 - * @param timestamp - */ - public void sendLog(long timestamp, String level, String source, String message) { - if (this.logSubscribers.isEmpty()) { - // nobody subscribed - return; - } - for (String id : this.logSubscribers) { - JsonArray jId = new JsonArray(); - jId.add("log"); - jId.add(id); - JsonObject j = DefaultMessages.log(jId, timestamp, level, source, message); - // TODO reevaluate if it is necessary to do this async; ie if websocket.send returns directly or not - logExecutor.execute(() -> { - this.send(j); - }); - } - } - -} +/******************************************************************************* + * 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.core.utilities.websocket; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.java_websocket.WebSocket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.HashMultimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.WriteChannel; +import io.openems.api.persistence.QueryablePersistence; +import io.openems.backend.timedata.api.TimedataService; +import io.openems.common.exceptions.AccessDeniedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; +import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.CurrentDataWorker; +import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.LogBehaviour; +import io.openems.common.websocket.Notification; +import io.openems.common.websocket.WebSocketUtils; +import io.openems.core.Config; +import io.openems.core.ConfigFormat; +import io.openems.core.ThingRepository; +import io.openems.core.utilities.ConfigUtils; +import io.openems.core.utilities.LinuxCommand; +import io.openems.core.utilities.api.ApiWorker; +import io.openems.core.utilities.api.WriteJsonObject; +import io.openems.core.utilities.api.WriteObject; + +/** + * Handles a Websocket connection to a browser, OpenEMS backend,... + * + * @author stefan.feilmeier + */ +public class EdgeWebsocketHandler { + + private Logger log = LoggerFactory.getLogger(EdgeWebsocketHandler.class); + + /** + * Holds the websocket connection + */ + protected Optional websocketOpt = Optional.empty(); + + /** + * Holds subscribers to current data + */ + private final HashMap currentDataSubscribers = new HashMap<>(); + + /** + * Holds subscribers to system log + */ + private final Set logSubscribers = new HashSet<>(); + + /** + * Executor for system log task + */ + private final ExecutorService logExecutor = Executors.newCachedThreadPool(); + + /** + * Predefined role for this connection. If empty, role is taken from message (in onMessage method). + */ + private final Optional roleOpt; + + /** + * ApiWorker handles updates to WriteChannels + */ + private final Optional apiWorkerOpt; + + public EdgeWebsocketHandler() { + this.apiWorkerOpt = Optional.empty(); + this.roleOpt = Optional.empty(); + } + + public EdgeWebsocketHandler(ApiWorker apiWorker, WebSocket websocket, Role role) { + this.apiWorkerOpt = Optional.ofNullable(apiWorker); + this.websocketOpt = Optional.ofNullable(websocket); + this.roleOpt = Optional.ofNullable(role); + } + + public void setWebsocket(WebSocket websocket) { + this.websocketOpt = Optional.ofNullable(websocket); + } + + public Optional getWebsocket() { + return websocketOpt; + } + + /** + * Handles a message from Websocket. + * + * @param jMessage + */ + public final void onMessage(JsonObject jMessage) { + // get message id -> used for reply + Optional jIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); + + // get role + Role role; + if (this.roleOpt.isPresent()) { + role = this.roleOpt.get(); + } else { + Optional roleStringOpt = JsonUtils.getAsOptionalString(jMessage, "role"); + if (roleStringOpt.isPresent()) { + role = Role.getRole(roleStringOpt.get()); + } else { + role = Role.getDefaultRole(); + } + } + + // prepare reply (every reply is going to be merged into this object with this unique message id) + JsonObject jReply = new JsonObject(); + + // init deviceId as empty. It's only needed in backend + Optional deviceIdOpt = Optional.empty(); + + /* + * Config + */ + Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); + if (jConfigOpt.isPresent()) { + jReply = JsonUtils.merge(jReply, // + config(jConfigOpt.get(), role, jIdOpt.orElse(new JsonArray()), apiWorkerOpt) // + ); + } + + /* + * Subscribe to currentData + */ + Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); + if (jCurrentDataOpt.isPresent() && jIdOpt.isPresent()) { + jReply = JsonUtils.merge(jReply, // + currentData(jIdOpt.get(), jCurrentDataOpt.get(), role) // + ); + } + + /* + * Query historic data + */ + // Optional jMessageIdOpt, String deviceName, WebSocket websocket, JsonElement jHistoricDataElement + Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); + if (jhistoricDataOpt.isPresent() && jIdOpt.isPresent()) { + // select first QueryablePersistence (by default the running InfluxdbPersistence) + TimedataService timedataSource = null; + for (QueryablePersistence queryablePersistence : ThingRepository.getInstance().getQueryablePersistences()) { + timedataSource = queryablePersistence; + break; + } + if (timedataSource == null) { + // TODO create notification that there is no datasource available + } else { + // TODO + // jReply = JsonUtils.merge(jReply, // + // WebSocketUtils.historicData(jIdOpt.get(), jhistoricDataOpt.get(), deviceIdOpt, timedataSource, + // role) // + // ); + } + } + + /* + * Subscribe to log + */ + Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log"); + if (jLogOpt.isPresent() && jIdOpt.isPresent()) { + try { + jReply = JsonUtils.merge(jReply, // + log(jIdOpt.get(), jLogOpt.get(), role) // + ); + } catch (AccessDeniedException e) { /* ignore */ } + } + + /* + * Remote system control + */ + { + Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system"); + if (jSystemOpt.isPresent() && jSystemOpt.isPresent()) { + try { + jReply = JsonUtils.merge(jReply, // + system(jIdOpt.get(), jSystemOpt.get(), role) // + ); + } catch (AccessDeniedException e) { + // TODO create notification + log.error(e.getMessage()); + } + } + } + + // send reply + if (jReply.entrySet().size() > 0) { + if (jIdOpt.isPresent()) { + jReply.add("id", jIdOpt.get()); + } + WebSocketUtils.send(this.websocketOpt, jReply); + } + } + + /** + * Handle "config" messages + * + * @param jConfig + * @return + */ + private synchronized JsonObject config(JsonObject jConfig, Role role, JsonArray jId, + Optional apiWorkerOpt) { + try { + String mode = JsonUtils.getAsString(jConfig, "mode"); + + if (mode.equals("query")) { + /* + * Query current config + */ + String language = JsonUtils.getAsString(jConfig, "language"); + JsonObject jReplyConfig = Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, role, language); + return DefaultMessages.configQueryReply(new JsonObject() /* TODO */, jReplyConfig); + + } else if (mode.equals("update")) { + try { + /* + * Update thing/channel config + */ + String thingId = JsonUtils.getAsString(jConfig, "thing"); + String channelId = JsonUtils.getAsString(jConfig, "channel"); + JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); + Optional channelOpt = ThingRepository.getInstance().getChannel(thingId, channelId); + if (channelOpt.isPresent()) { + Channel channel = channelOpt.get(); + // check write permissions + channel.assertWriteAllowed(role); + if (channel instanceof ConfigChannel) { + /* + * ConfigChannel + */ + ConfigChannel configChannel = (ConfigChannel) channel; + Object value = ConfigUtils.getConfigObject(configChannel, jValue); + configChannel.updateValue(value, true); + WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue); + + } else if (channel instanceof WriteChannel) { + /* + * WriteChannel + */ + WriteChannel writeChannel = (WriteChannel) channel; + if (!apiWorkerOpt.isPresent()) { + WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, + Notification.BACKEND_NOT_ALLOWED, "set " + channel.address() + " => " + jValue); + } else { + ApiWorker apiWorker = apiWorkerOpt.get(); + WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> { + WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_SUCCESS, + "set " + channel.address() + " => " + jValue); + }).onFirstError((e) -> { + WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_FAILED, + "set " + channel.address() + " => " + jValue, e.getMessage()); + }).onTimeout(() -> { + WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, + "set " + channel.address() + " => " + jValue); + }); + apiWorker.addValue(writeChannel, writeObject); + } + } + } else { + throw new OpenemsException("Unable to find Channel [" + thingId + "/" + channelId + "]"); + } + } catch (OpenemsException e) { + WebSocketUtils.send(websocketOpt, + DefaultMessages.notification(new JsonObject() /* TODO */, Notification.EDGE_CHANNEL_UPDATE_FAILED, e.getMessage())); + } + } + } catch (OpenemsException e) { + log.warn(e.getMessage()); + } + return new JsonObject(); + } + + /** + * Handle current data subscriptions + * (try to keep synced with Backend.BrowserWebsocket) + * + * @param j + */ + private synchronized JsonObject currentData(JsonArray jId, JsonObject jCurrentData, Role role) { + try { + String mode = JsonUtils.getAsString(jCurrentData, "mode"); + + if (mode.equals("subscribe")) { + /* + * Subscribe to channels + */ + String messageId = jId.get(jId.size() - 1).getAsString(); + + // remove old worker if existed + CurrentDataWorker worker = this.currentDataSubscribers.remove(messageId); + if (worker != null) { + worker.dispose(); + } + // parse subscribed channels + HashMultimap channels = HashMultimap.create(); + JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); + for (Entry entry : jSubscribeChannels.entrySet()) { + String thing = entry.getKey(); + JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement jChannel : jChannels) { + String channel = JsonUtils.getAsString(jChannel); + channels.put(thing, channel); + } + } + if (!channels.isEmpty()) { + // create new worker + worker = new EdgeCurrentDataWorker(new JsonObject() /* TODO */, channels, role, this); + this.currentDataSubscribers.put(messageId, worker); + } + } + } catch (OpenemsException e) { + log.warn(e.getMessage()); + } + return new JsonObject(); + } + + /** + * Handle system log subscriptions + * + * @param j + * @throws AccessDeniedException + */ + private synchronized JsonObject log(JsonArray jId, JsonObject jLog, Role role) throws AccessDeniedException { + if (!(role == Role.ADMIN || role == Role.INSTALLER || role == Role.OWNER)) { + throw new AccessDeniedException("User role [" + role + "] is not allowed to read system logs."); + } + try { + String mode = JsonUtils.getAsString(jLog, "mode"); + String messageId = jId.get(jId.size() - 1).getAsString(); + + if (mode.equals("subscribe")) { + /* + * Subscribe to system log + */ + this.logSubscribers.add(messageId); + } else if (mode.equals("unsubscribe")) { + /* + * Unsubscribe from system log + */ + this.logSubscribers.remove(messageId); + } + } catch (OpenemsException e) { + log.warn(e.getMessage()); + } + return new JsonObject(); + } + + /** + * Handle remote system control + * + * @param j + * @throws AccessDeniedException + */ + private synchronized JsonObject system(JsonArray jId, JsonObject jSystem, Role role) throws AccessDeniedException { + if (!(role == Role.ADMIN)) { + throw new AccessDeniedException("User role [" + role + "] is not allowed to execute system commands."); + } + String output = ""; + try { + String mode = JsonUtils.getAsString(jSystem, "mode"); + String password = JsonUtils.getAsString(jSystem, "password"); + String command = JsonUtils.getAsString(jSystem, "command"); + boolean background = JsonUtils.getAsBoolean(jSystem, "background"); + int timeout = JsonUtils.getAsInt(jSystem, "timeout"); + + if (mode.equals("execute")) { + output = LinuxCommand.execute(password, command, background, timeout); + } + } catch (OpenemsException e) { + output += e.getMessage(); + } + return DefaultMessages.systemExecuteReply(new JsonObject() /* TODO */, output); + } + + // TODO handle config command + // /** + // * Set configuration + // * + // * @param j + // */ + // private synchronized void configure(JsonElement jConfigsElement) { + // try { + // JsonArray jConfigs = JsonUtils.getAsJsonArray(jConfigsElement); + // ThingRepository thingRepository = ThingRepository.getInstance(); + // for (JsonElement jConfigElement : jConfigs) { + // JsonObject jConfig = JsonUtils.getAsJsonObject(jConfigElement); + // String mode = JsonUtils.getAsString(jConfig, "mode"); + // if (mode.equals("update")) { + // /* + // * Channel Set mode + // */ + // String thingId = JsonUtils.getAsString(jConfig, "thing"); + // String channelId = JsonUtils.getAsString(jConfig, "channel"); + // JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); + // Optional channelOptional = thingRepository.getChannel(thingId, channelId); + // if (channelOptional.isPresent()) { + // Channel channel = channelOptional.get(); + // if (channel instanceof ConfigChannel) { + // /* + // * ConfigChannel + // */ + // ConfigChannel configChannel = (ConfigChannel) channel; + // configChannel.updateValue(jValue, true); + // Notification.send(NotificationType.SUCCESS, + // "Successfully updated [" + channel.address() + "] to [" + jValue + "]"); + // + // } else if (channel instanceof WriteChannel) { + // /* + // * WriteChannel + // */ + // WriteChannel writeChannel = (WriteChannel) channel; + // writeChannel.pushWrite(jValue); + // Notification.send(NotificationType.SUCCESS, + // "Successfully set [" + channel.address() + "] to [" + jValue + "]"); + // } + // } else { + // throw new ConfigException("Unable to find " + jConfig.toString()); + // } + // } else if (mode.equals("create")) { + // /* + // * Create new Thing + // */ + // JsonObject jObject = JsonUtils.getAsJsonObject(jConfig, "object"); + // String parentId = JsonUtils.getAsString(jConfig, "parent"); + // String thingId = JsonUtils.getAsString(jObject, "id"); + // if (thingId.startsWith("_")) { + // throw new ConfigException("IDs starting with underscore are reserved for internal use."); + // } + // if (thingRepository.getThingById(thingId).isPresent()) { + // throw new ConfigException("Thing Id is already existing."); + // } + // String clazzName = JsonUtils.getAsString(jObject, "class"); + // Class clazz = Class.forName(clazzName); + // if (Device.class.isAssignableFrom(clazz)) { + // // Device + // Thing parentThing = thingRepository.getThing(parentId); + // if (parentThing instanceof Bridge) { + // Bridge parentBridge = (Bridge) parentThing; + // Device device = thingRepository.createDevice(jObject); + // parentBridge.addDevice(device); + // Config.getInstance().writeConfigFile(); + // Notification.send(NotificationType.SUCCESS, "Device [" + device.id() + "] wurde erstellt."); + // break; + // } + // } + // } else if (mode.equals("delete")) { + // /* + // * Delete a Thing + // */ + // String thingId = JsonUtils.getAsString(jConfig, "thing"); + // thingRepository.removeThing(thingId); + // Config.getInstance().writeConfigFile(); + // Notification.send(NotificationType.SUCCESS, "Controller [" + thingId + "] wurde " + " gel�scht."); + // } else { + // throw new OpenemsException("Modus [" + mode + "] ist nicht implementiert."); + // } + // } + // // Send new config + // JsonObject jMetadata = new JsonObject(); + // // TODO jMetadata.add("config", Config.getInstance().getMetaConfigJson()); + // JsonObject j = new JsonObject(); + // j.add("metadata", jMetadata); + // WebSocketUtils.send(this.websocket, j); + // } catch (OpenemsException | ClassNotFoundException e) { + // Notification.send(NotificationType.ERROR, e.getMessage()); + // } + // } + + // TODO handle system command + // /** + // * System command + // * + // * @param j + // */ + // private synchronized void system(JsonElement jSystemElement) { + // JsonObject jNotification = new JsonObject(); + // try { + // JsonObject jSystem = JsonUtils.getAsJsonObject(jSystemElement); + // String mode = JsonUtils.getAsString(jSystem, "mode"); + // if (mode.equals("systemd-restart")) { + // /* + // * Restart systemd service + // */ + // String service = JsonUtils.getAsString(jSystem, "service"); + // if (service.equals("fems-pagekite")) { + // ProcessBuilder builder = new ProcessBuilder("/bin/systemctl", "restart", "fems-pagekite"); + // Process p = builder.start(); + // if (p.waitFor() == 0) { + // log.info("Successfully restarted fems-pagekite"); + // } else { + // throw new OpenemsException("restart fems-pagekite failed"); + // } + // } else { + // throw new OpenemsException("Unknown systemd-restart service: " + jSystemElement.toString()); + // } + // + // } else if (mode.equals("manualpq")) { + // /* + // * Manual PQ settings + // */ + // String ess = JsonUtils.getAsString(jSystem, "ess"); + // Boolean active = JsonUtils.getAsBoolean(jSystem, "active"); + // if (active) { + // Long p = JsonUtils.getAsLong(jSystem, "p"); + // Long q = JsonUtils.getAsLong(jSystem, "q"); + // if (this.controller == null) { + // throw new OpenemsException("Local access only. Controller is null."); + // } + // this.controller.setManualPQ(ess, p, q); + // Notification.send(NotificationType.SUCCESS, + // "Leistungsvorgabe gesetzt: ess[" + ess + "], p[" + p + "], q[" + q + "]"); + // } else { + // this.controller.resetManualPQ(ess); + // Notification.send(NotificationType.SUCCESS, "Leistungsvorgabe gestoppt: ess[" + ess + "]"); + // } + // } else { + // throw new OpenemsException("Unknown system message: " + jSystemElement.toString()); + // } + // } catch (OpenemsException | IOException | InterruptedException e) { + // Notification.send(NotificationType.ERROR, e.getMessage()); + // } + // } + + // TODO handle manual PQ + // private void manualPQ(JsonElement j, AuthenticatedWebsocketHandler handler) { + // try { + // JsonObject jPQ = JsonUtils.getAsJsonObject(j); + // if (jPQ.has("p") && jPQ.has("q")) { + // long p = JsonUtils.getAsLong(jPQ, "p"); + // long q = JsonUtils.getAsLong(jPQ, "q"); + // this.controller.setManualPQ(p, q); + // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabe gesetzt: P=" + p + ",Q=" + q); + // } else { + // // stop manual PQ + // this.controller.resetManualPQ(); + // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabe zurückgesetzt"); + // } + // } catch (ReflectionException e) { + // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabewerte falsch: " + e.getMessage()); + // } + // } + + // TODO handle channel commands + // private void channel(JsonElement jChannelElement, AuthenticatedWebsocketHandler handler) { + // try { + // JsonObject jChannel = JsonUtils.getAsJsonObject(jChannelElement); + // String thingId = JsonUtils.getAsString(jChannel, "thing"); + // String channelId = JsonUtils.getAsString(jChannel, "channel"); + // JsonElement jValue = JsonUtils.getSubElement(jChannel, "value"); + // + // // get channel + // Channel channel; + // Optional channelOptional = thingRepository.getChannel(thingId, channelId); + // if (channelOptional.isPresent()) { + // // get channel value + // channel = channelOptional.get(); + // } else { + // // Channel not found + // throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); + // } + // + // // check for writable channel + // if (!(channel instanceof WriteChannel)) { + // throw new ResourceException(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + // } + // + // // set channel value + // if (channel instanceof ConfigChannel) { + // // is a ConfigChannel + // ConfigChannel configChannel = (ConfigChannel) channel; + // try { + // configChannel.updateValue(jValue, true); + // log.info("Updated Channel [" + channel.address() + "] to value [" + jValue.toString() + "]."); + // handler.sendNotification(NotificationType.SUCCESS, + // "Channel [" + channel.address() + "] aktualisiert zu [" + jValue.toString() + "]."); + // } catch (NotImplementedException e) { + // throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Conversion not implemented"); + // } + // } else { + // // is a WriteChannel + // handler.sendNotification(NotificationType.WARNING, "WriteChannel nicht implementiert"); + // } + // } catch (ReflectionException e) { + // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabewerte falsch: " + e.getMessage()); + // } + // } + + /** + * Send a notification message/error to the websocket + * + * @param mesage + * @return true if successful, otherwise false + */ + // TODO send notification + // public synchronized void sendNotification(NotificationType type, String message) { + // JsonObject jNotification = new JsonObject(); + // jNotification.addProperty("type", type.name().toLowerCase()); + // jNotification.addProperty("message", message); + // JsonObject j = new JsonObject(); + // j.add("notification", jNotification); + // new Thread(() -> { + // WebSocketUtils.send(websocket, j); + // }).start(); + // } + + /** + * Send a message to the websocket. + * + * @param message + */ + public boolean send(JsonObject j) { + return WebSocketUtils.send(this.websocketOpt, j); + } + + /** + * Send a log message to the websocket. This method is called by logback + * + * @param message2 + * @param timestamp + */ + public void sendLog(long timestamp, String level, String source, String message) { + if (this.logSubscribers.isEmpty()) { + // nobody subscribed + return; + } + for (String id : this.logSubscribers) { + JsonArray jId = new JsonArray(); + jId.add("log"); + jId.add(id); + JsonObject j = DefaultMessages.log(new JsonObject() /* TODO */, timestamp, level, source, message); + // TODO reevaluate if it is necessary to do this async; ie if websocket.send returns directly or not + logExecutor.execute(() -> { + this.send(j); + }); + } + } + +} diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java index 31c022660da..0be2399aac1 100644 --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java @@ -1,196 +1,188 @@ -/******************************************************************************* - * 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.api.websocket; - -import java.util.ArrayList; -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.handshake.ClientHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.utils.JsonUtils; -import io.openems.common.websocket.AbstractWebsocketServer; -import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.LogBehaviour; -import io.openems.common.websocket.Notification; -import io.openems.common.websocket.WebSocketUtils; -import io.openems.core.utilities.api.ApiWorker; -import io.openems.impl.controller.api.websocket.session.WebsocketApiSession; -import io.openems.impl.controller.api.websocket.session.WebsocketApiSessionData; -import io.openems.impl.controller.api.websocket.session.WebsocketApiSessionManager; - -public class WebsocketApiServer -extends AbstractWebsocketServer { - - private static Logger log = LoggerFactory.getLogger(WebsocketApiServer.class); - private final ApiWorker apiWorker; - - public WebsocketApiServer(ApiWorker apiWorker, int port) { - super(port, new WebsocketApiSessionManager()); - this.apiWorker = apiWorker; - } - - /** - * Open event of websocket. Parses the Odoo "session_id" and stores it in a new Session. - */ - @Override - protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - JsonObject jHandshake = this.parseCookieFromHandshake(handshake); - Optional tokenOpt = JsonUtils.getAsOptionalString(jHandshake, "token"); - if (tokenOpt.isPresent()) { - String token = tokenOpt.get(); - Optional sessionOpt = this.sessionManager.getSessionByToken(token); - if (sessionOpt.isPresent()) { - WebsocketApiSession session = sessionOpt.get(); - Optional oldWebsocketOpt = session.getData().getWebsocketHandler().getWebsocket(); - if (oldWebsocketOpt.isPresent()) { - // TODO to avoid this, websockets needs to be able to handle more than one websocket per session - oldWebsocketOpt.get().closeConnection(CloseFrame.REFUSE, - "Another client connected with this token"); - } - // refresh session - session.getData().getWebsocketHandler().setWebsocket(websocket); - // add to websockets - this.addWebsocket(websocket, session); - // send connection successful to browser - JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), - Optional.of(session.getData().getRole()), new ArrayList<>()); - log.info("Websocket connected by session. User [" + session.getData().getUser() + "] Session [" - + session.getToken() + "]"); - WebSocketUtils.send(websocket, jReply); - return; - } - // if we are here, automatic authentication was not possible -> notify client - WebSocketUtils.sendNotification(websocket, new JsonArray(), LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_AUTHENTICATION_BY_TOKEN_FAILED, tokenOpt.orElse("")); - } - } - - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage, Optional jMessageIdOpt, - Optional deviceNameOpt) { - // log.info("RECV: websocket[" + websocket + "]" + jMessage.toString()); - - /* - * Authenticate - */ - Optional sessionOpt = Optional.empty(); - if (jMessage.has("authenticate")) { - // authenticate by username/password - sessionOpt = authenticate(jMessage.get("authenticate"), websocket); - } - if (!sessionOpt.isPresent()) { - // check if there is an existing session - sessionOpt = this.getSessionFromWebsocket(websocket); - } - if (!sessionOpt.isPresent()) { - /* - * send authentication failed reply - */ - JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); - WebSocketUtils.send(websocket, jReply); - websocket.closeConnection(CloseFrame.REFUSE, "Authentication failed"); - return; - } - WebsocketApiSession session = sessionOpt.get(); - /* - * On initial authentication... - */ - if (jMessage.has("authenticate")) { - // add to websockets - this.addWebsocket(websocket, session); - // send connection successful to browser - JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), - Optional.of(session.getData().getRole()), new ArrayList<>()); - log.info("Browser connected by authentication. User [" + session.getData().getUser() + "] Session [" - + session.getToken() + "]"); - WebSocketUtils.send(websocket, jReply); - } - - /* - * Rest -> forward to websocket handler - */ - session.getData().getWebsocketHandler().onMessage(jMessage); - } - - @Override - protected void _onClose(WebSocket websocket, Optional sessionOpt) { - // nothing to do here - } - - /** - * Authenticates a user according to the "authenticate" message. Stores the session if valid. - * - * @param jAuthenticateElement - * @param handler - */ - private Optional authenticate(JsonElement jAuthenticateElement, WebSocket websocket) { - try { - JsonObject jAuthenticate = JsonUtils.getAsJsonObject(jAuthenticateElement); - if (jAuthenticate.has("mode")) { - String mode = JsonUtils.getAsString(jAuthenticate, "mode"); - if (mode.equals("login")) { - if (jAuthenticate.has("password")) { - /* - * Authenticate using username and password - */ - String password = JsonUtils.getAsString(jAuthenticate, "password"); - if (jAuthenticate.has("username")) { - String username = JsonUtils.getAsString(jAuthenticate, "username"); - return this.sessionManager.authByUserPassword(username, password, websocket, apiWorker); - } else { - return this.sessionManager.authByPassword(password, websocket, apiWorker); - } - } - - } else if (mode.equals("logout")) { - /* - * Logout and close session - */ - this.removeWebsocket(websocket); - } - } - } catch (OpenemsException e) { /* ignore */ } - return Optional.empty(); - } - - /** - * Send a log entry to all connected websockets - * - * @param string - * @param timestamp - * - * @param jMessage - */ - public void broadcastLog(long timestamp, String level, String source, String message) { - for (WebsocketApiSession session : this.sessionManager.getSessions()) { - session.getData().getWebsocketHandler().sendLog(timestamp, level, source, message); - } - } -} +/******************************************************************************* + * 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.api.websocket; + +import java.util.Optional; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.websocket.AbstractWebsocketServer; +import io.openems.core.utilities.api.ApiWorker; +import io.openems.impl.controller.api.websocket.session.WebsocketApiSession; + +public class WebsocketApiServer extends AbstractWebsocketServer { + + private static Logger log = LoggerFactory.getLogger(WebsocketApiServer.class); + private final ApiWorker apiWorker; + + public WebsocketApiServer(ApiWorker apiWorker, int port) { + super(port); + this.apiWorker = apiWorker; + } + + /** + * Open event of websocket. Parses the Odoo "session_id" and stores it in a new Session. + */ + @Override + protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + // JsonObject jHandshake = this.parseCookieFromHandshake(handshake); + // Optional tokenOpt = JsonUtils.getAsOptionalString(jHandshake, "token"); + // if (tokenOpt.isPresent()) { + // String token = tokenOpt.get(); + // Optional sessionOpt = this.sessionManager.getSessionByToken(token); + // if (sessionOpt.isPresent()) { + // WebsocketApiSession session = sessionOpt.get(); + // Optional oldWebsocketOpt = session.getData().getWebsocketHandler().getWebsocket(); + // if (oldWebsocketOpt.isPresent()) { + // // TODO to avoid this, websockets needs to be able to handle more than one websocket per session + // oldWebsocketOpt.get().closeConnection(CloseFrame.REFUSE, + // "Another client connected with this token"); + // } + // // refresh session + // session.getData().getWebsocketHandler().setWebsocket(websocket); + // // add to websockets + // this.addWebsocket(websocket, session); + // // send connection successful to browser + // JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), + // Optional.of(session.getData().getRole()), new JsonArray()); + // log.info("Websocket connected by session. User [" + session.getData().getUser() + "] Session [" + // + session.getToken() + "]"); + // WebSocketUtils.send(websocket, jReply); + // return; + // } + // // if we are here, automatic authentication was not possible -> notify client + // WebSocketUtils.sendNotification(websocket, new JsonArray(), LogBehaviour.WRITE_TO_LOG, + // Notification.EDGE_AUTHENTICATION_BY_TOKEN_FAILED, tokenOpt.orElse("")); + // } + } + + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage) { + // // log.info("RECV: websocket[" + websocket + "]" + jMessage.toString()); + // + // /* + // * Authenticate + // */ + // Optional sessionOpt = Optional.empty(); + // if (jMessage.has("authenticate")) { + // // authenticate by username/password + // sessionOpt = authenticate(jMessage.get("authenticate"), websocket); + // } + // if (!sessionOpt.isPresent()) { + // // check if there is an existing session + // sessionOpt = this.getSessionFromWebsocket(websocket); + // } + // if (!sessionOpt.isPresent()) { + // /* + // * send authentication failed reply + // */ + // JsonObject jReply = DefaultMessages.browserConnectionFailedReply(); + // WebSocketUtils.send(websocket, jReply); + // websocket.closeConnection(CloseFrame.REFUSE, "Authentication failed"); + // return; + // } + // WebsocketApiSession session = sessionOpt.get(); + // /* + // * On initial authentication... + // */ + // if (jMessage.has("authenticate")) { + // // add to websockets + // this.addWebsocket(websocket, session); + // // send connection successful to browser + // JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(), + // Optional.of(session.getData().getRole()), new ArrayList<>()); + // log.info("Browser connected by authentication. User [" + session.getData().getUser() + "] Session [" + // + session.getToken() + "]"); + // WebSocketUtils.send(websocket, jReply); + // } + // + // /* + // * Rest -> forward to websocket handler + // */ + // session.getData().getWebsocketHandler().onMessage(jMessage); + } + + @Override + protected void _onClose(WebSocket websocket) { + // nothing to do here + } + + /** + * Authenticates a user according to the "authenticate" message. Stores the session if valid. + * + * @param jAuthenticateElement + * @param handler + */ + private Optional authenticate(JsonElement jAuthenticateElement, WebSocket websocket) { + // try { + // JsonObject jAuthenticate = JsonUtils.getAsJsonObject(jAuthenticateElement); + // if (jAuthenticate.has("mode")) { + // String mode = JsonUtils.getAsString(jAuthenticate, "mode"); + // if (mode.equals("login")) { + // if (jAuthenticate.has("password")) { + // /* + // * Authenticate using username and password + // */ + // String password = JsonUtils.getAsString(jAuthenticate, "password"); + // if (jAuthenticate.has("username")) { + // String username = JsonUtils.getAsString(jAuthenticate, "username"); + // return this.sessionManager.authByUserPassword(username, password, websocket, apiWorker); + // } else { + // return this.sessionManager.authByPassword(password, websocket, apiWorker); + // } + // } + // + // } else if (mode.equals("logout")) { + // /* + // * Logout and close session + // */ + // this.removeWebsocket(websocket); + // } + // } + // } catch (OpenemsException e) { /* ignore */ } + return Optional.empty(); + } + + /** + * Send a log entry to all connected websockets + * + * @param string + * @param timestamp + * + * @param jMessage + */ + public void broadcastLog(long timestamp, String level, String source, String message) { + // for (WebsocketApiSession session : this.sessionManager.getSessions()) { + // session.getData().getWebsocketHandler().sendLog(timestamp, level, source, message); + // } + } + + @Override + protected void _onError(WebSocket websocket, Exception ex) { + // TODO Auto-generated method stub + } +} diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index 77d95a570c6..7d101310dfc 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -114,7 +114,7 @@ public FeneconPersistence() { try { WebSocketUtils.send( // websocket, // - DefaultMessages.configQueryReply(Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, + DefaultMessages.configQueryReply(new JsonObject() /* TODO */, Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, Role.ADMIN, DEFAULT_CONFIG_LANGUAGE))); log.info("Sent config to FENECON persistence."); } catch (NotImplementedException | ConfigException e) { diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java index cebf89320a3..6793721a7e7 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java @@ -44,8 +44,8 @@ import io.openems.api.doc.ThingInfo; import io.openems.api.exception.OpenemsException; import io.openems.api.persistence.QueryablePersistence; +import io.openems.backend.timedata.influx.InfluxdbUtils; import io.openems.common.types.ChannelEnum; -import io.openems.common.utils.InfluxdbUtils; import io.openems.core.Databus; @ThingInfo(title = "InfluxDB Persistence", description = "Persists data in an InfluxDB time-series database.") @@ -204,7 +204,7 @@ private Optional getInfluxDB() { } @Override - public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, + public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws io.openems.common.exceptions.OpenemsException { Optional influxdbOpt = getInfluxDB(); if (!influxdbOpt.isPresent()) { @@ -214,7 +214,7 @@ public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime if (!databaseOpt.isPresent()) { throw new OpenemsException("InfluxDB database is not available"); } - return InfluxdbUtils.queryHistoricData(influxdbOpt.get(), databaseOpt.get(), deviceIdOpt, fromDate, toDate, + return InfluxdbUtils.queryHistoricData(influxdbOpt.get(), databaseOpt.get(), edgeId, fromDate, toDate, channels, resolution); } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java index 524d5080432..8f4dd69300b 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java +++ b/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/User.java @@ -5,6 +5,8 @@ import java.util.Optional; import java.util.TreeMap; +import io.openems.common.session.Role; + public class User { private final int id; private String name; diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 482c0accf31..648d0f5a9a0 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -34,9 +34,9 @@ import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.Edge; import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.metadata.api.Role; import io.openems.backend.metadata.api.User; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; @Designate(ocd = Odoo.Config.class, factory = false) diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java index 16c271f631b..f3bbbfc7f51 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java @@ -6,6 +6,7 @@ import com.google.common.collect.HashMultimap; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.openems.common.exceptions.NotImplementedException; import io.openems.common.types.ChannelAddress; @@ -17,9 +18,9 @@ public class BackendCurrentDataWorker extends CurrentDataWorker { private final UiWebsocketServer parent; private final int edgeId; - public BackendCurrentDataWorker(UiWebsocketServer parent, WebSocket websocket, String messageId, int edgeId, + public BackendCurrentDataWorker(UiWebsocketServer parent, WebSocket websocket, JsonObject jMessageId, int edgeId, HashMultimap channels) { - super(websocket, messageId, channels); + super(websocket, jMessageId, channels); this.parent = parent; this.edgeId = edgeId; } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 888bfc038b9..d0e51ce866c 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -22,9 +22,9 @@ import com.google.gson.JsonObject; import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Role; import io.openems.backend.metadata.api.User; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; import io.openems.common.utils.StringUtils; import io.openems.common.websocket.AbstractWebsocketServer; @@ -191,7 +191,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * Query current config */ - JsonObject jReply = DefaultMessages.configQueryReply(messageId, edge.getConfig()); + JsonObject jReply = DefaultMessages.configQueryReply(new JsonObject() /* TODO */, edge.getConfig()); WebSocketUtils.send(websocket, jReply); break; } @@ -248,7 +248,7 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, S } if (!channels.isEmpty()) { // create new worker - BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, messageId, edgeId, + BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, new JsonObject() /* TODO */, edgeId, channels); data.setCurrentDataWorker(worker); } @@ -292,7 +292,7 @@ private JsonObject historicData(String jMessageId, int edgeId, JsonObject jHisto JsonArray jData = this.parent.timeDataService.queryHistoricData(edgeId, fromDate, toDate, channels, resolution); // send reply - return DefaultMessages.historicDataQueryReply(jMessageId, jData); + return DefaultMessages.historicDataQueryReply(new JsonObject() /* TODO */, jData); } } catch (Exception e) { // TODO handle exception diff --git a/common/src/io/openems/common/exceptions/AccessDeniedException.java b/io.openems.common/src/io/openems/common/exceptions/AccessDeniedException.java similarity index 100% rename from common/src/io/openems/common/exceptions/AccessDeniedException.java rename to io.openems.common/src/io/openems/common/exceptions/AccessDeniedException.java diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Role.java b/io.openems.common/src/io/openems/common/session/Role.java similarity index 91% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Role.java rename to io.openems.common/src/io/openems/common/session/Role.java index 8617b49bf83..2973ec50013 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Role.java +++ b/io.openems.common/src/io/openems/common/session/Role.java @@ -1,4 +1,4 @@ -package io.openems.backend.metadata.api; +package io.openems.common.session; public enum Role { ADMIN, INSTALLER, OWNER, GUEST; diff --git a/common/src/io/openems/common/types/ChannelEnum.java b/io.openems.common/src/io/openems/common/types/ChannelEnum.java similarity index 100% rename from common/src/io/openems/common/types/ChannelEnum.java rename to io.openems.common/src/io/openems/common/types/ChannelEnum.java diff --git a/io.openems.common/src/io/openems/common/types/DeviceImpl.java b/io.openems.common/src/io/openems/common/types/DeviceImpl.java index eac3644602b..135ceeae8b6 100644 --- a/io.openems.common/src/io/openems/common/types/DeviceImpl.java +++ b/io.openems.common/src/io/openems/common/types/DeviceImpl.java @@ -27,7 +27,7 @@ public String getName() { } // public Role getRole() { -//// return role; +// return role; // } public void setOnline(boolean online) { 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 c98c78deeea..f22614e80d5 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -22,6 +22,7 @@ import io.openems.common.exceptions.NotImplementedException; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelEnum; // TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions public class JsonUtils { @@ -307,6 +308,11 @@ public static JsonElement getAsJsonElement(Object value) throws NotImplementedEx * Number */ return new JsonPrimitive((Number) value); + } else if(value instanceof ChannelEnum) { + /* + * ChannelEnum + */ + return new JsonPrimitive(((ChannelEnum)value).getValue()); } else if (value instanceof String) { /* * String diff --git a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java index 6ccd8391615..fba72c67eca 100644 --- a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -34,7 +34,7 @@ public abstract class CurrentDataWorker { private final WebSocket websocket; - public CurrentDataWorker(WebSocket websocket, String messageId, HashMultimap channels) { + public CurrentDataWorker(WebSocket websocket, JsonObject jMessageId, HashMultimap channels) { this.websocket = websocket; this.channels = channels; this.future = this.executor.scheduleWithFixedDelay(() -> { @@ -46,7 +46,7 @@ public CurrentDataWorker(WebSocket websocket, String messageId, HashMultimap * { + * messageId: {}, * config: { * ... * } @@ -167,8 +168,8 @@ public static JsonObject timestampedData(long timestamp, HashMap * { - * messageId: UUID, + * messageId: {}, * currentData: {[{ * channel: string, * value: any @@ -186,8 +187,8 @@ public static JsonObject configQueryReply(String messageId, JsonObject config) { * * @return */ - public static JsonObject currentData(String messageId, JsonObject jCurrentData) { - JsonObject j = newMessage(messageId); + public static JsonObject currentData(JsonObject jMessageId, JsonObject jCurrentData) { + JsonObject j = newMessage(jMessageId); j.add("currentData", jCurrentData); return j; } @@ -195,7 +196,7 @@ public static JsonObject currentData(String messageId, JsonObject jCurrentData) /** *
         	 *	{
        -	 *		messageId: UUID,
        +	 *		messageId: {},
         	 *		historicData: {
         	 *			data: [{
         	 *				time: ...,
        @@ -211,8 +212,8 @@ public static JsonObject currentData(String messageId, JsonObject jCurrentData)
         	 * 
         	 * @return
         	 */
        -	public static JsonObject historicDataQueryReply(String messageId, JsonArray jData) {
        -		JsonObject j = newMessage(messageId);
        +	public static JsonObject historicDataQueryReply(JsonObject jMessageId, JsonArray jData) {
        +		JsonObject j = newMessage(jMessageId);
         		JsonObject jHistoricData = new JsonObject();
         		jHistoricData.add("data", jData);
         		j.add("historicData", jHistoricData);
        @@ -222,8 +223,8 @@ public static JsonObject historicDataQueryReply(String messageId, JsonArray jDat
         	/**
         	 * 
         	 *	{
        +	 *		messageId: {},
         	 *		notification: {
        -	 *			id: string[],
         	 *			type: string,
         	 *			message: string,
         	 *			code: number,
        @@ -234,9 +235,8 @@ public static JsonObject historicDataQueryReply(String messageId, JsonArray jDat
         	 * 
         	 * @return
         	 */
        -	public static JsonObject notification(JsonArray jId, Notification code, String message, Object... params) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        +	public static JsonObject notification(JsonObject jMessageId, Notification code, String message, Object... params) {
        +		JsonObject j = newMessage(jMessageId);
         		JsonObject jNotification = new JsonObject();
         		jNotification.addProperty("type", code.getType().toString().toLowerCase());
         		jNotification.addProperty("message", message);
        @@ -253,6 +253,7 @@ public static JsonObject notification(JsonArray jId, Notification code, String m
         	/**
         	 * 
         	 *	{
        +	 *		messageId: {}
         	 *		currentData: {
         	 *			mode: 'subscribe',
         	 *			channels: {}
        @@ -262,9 +263,8 @@ public static JsonObject notification(JsonArray jId, Notification code, String m
         	 * 
         	 * @return
         	 */
        -	public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannels) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        +	public static JsonObject currentDataSubscribe(JsonObject jMessageId, JsonObject jChannels) {
        +		JsonObject j = newMessage(jMessageId);
         		JsonObject jCurrentData = new JsonObject();
         		jCurrentData.addProperty("mode", "subscribe");
         		jCurrentData.add("channels", jChannels);
        @@ -275,7 +275,7 @@ public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannel
         	/**
         	 * 
         	 *	{
        -	 *		id: [string],
        +	 *		messageId: {},
         	 *		log: {
         	 *			times: number,
         	 *			level: string,
        @@ -287,9 +287,8 @@ public static JsonObject currentDataSubscribe(JsonArray jId, JsonObject jChannel
         	 * 
         	 * @return
         	 */
        -	public static JsonObject log(JsonArray jId, long timestamp, String level, String source, String message) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        +	public static JsonObject log(JsonObject jMessageId, long timestamp, String level, String source, String message) {
        +		JsonObject j = newMessage(jMessageId);
         		JsonObject jLog = new JsonObject();
         		jLog.addProperty("time", timestamp);
         		jLog.addProperty("level", level);
        @@ -302,7 +301,7 @@ public static JsonObject log(JsonArray jId, long timestamp, String level, String
         	/**
         	 * 
         	 *	{
        -	 *		id: [string],
        +	 *		messageId: {},
         	 *		log: {
         	 *			mode: "unsubscribe"
         	 *		}
        @@ -311,9 +310,8 @@ public static JsonObject log(JsonArray jId, long timestamp, String level, String
         	 * 
         	 * @return
         	 */
        -	public static JsonObject logUnsubscribe(JsonArray jId) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        +	public static JsonObject logUnsubscribe(JsonObject jMessageId) {
        +		JsonObject j = newMessage(jMessageId);
         		JsonObject jLog = new JsonObject();
         		jLog.addProperty("mode", "unsubscribe");
         		j.add("log", jLog);
        @@ -323,7 +321,7 @@ public static JsonObject logUnsubscribe(JsonArray jId) {
         	/**
         	 * 
         	 *	{
        -	 *		id: [string],
        +	 *		messageId: {},
         	 *		system: {
         	 *			mode: "executeReply",
         	 *			output: string
        @@ -333,9 +331,8 @@ public static JsonObject logUnsubscribe(JsonArray jId) {
         	 * 
         	 * @return
         	 */
        -	public static JsonObject systemExecuteReply(JsonArray jId, String output) {
        -		JsonObject j = new JsonObject();
        -		j.add("id", jId);
        +	public static JsonObject systemExecuteReply(JsonObject jMessageId, String output) {
        +		JsonObject j = newMessage(jMessageId);
         		JsonObject jSystem = new JsonObject();
         		jSystem.addProperty("mode", "executeReply");
         		jSystem.addProperty("output", output);
        diff --git a/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java
        index a15b206c92c..ce07ca1b057 100644
        --- a/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java
        +++ b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java
        @@ -43,7 +43,7 @@ public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBe
         			notification.writeToLog(log, params);
         		}
         		String message = String.format(notification.getMessage(), params);
        -		JsonObject j = DefaultMessages.notification(jId, notification, message, params);
        +		JsonObject j = DefaultMessages.notification(new JsonObject() /* TODO */, notification, message, params);
         		return WebSocketUtils.send(websocket, j);
         	}
         
        
        From 414e32ce921e20104bcde5759cba4bf6ba6b37c2 Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Sat, 24 Feb 2018 22:49:44 +0100
        Subject: [PATCH 098/156] Clean old backend component; leaving only whats not
         migrated yet
        
        ---
         backend/.classpath                            |  55 ++++---
         backend/OpenEMS-Backend (Dummy).launch        |  28 ----
         backend/OpenEMS-Backend (File).launch         |  29 ----
         backend/OpenEMS-Backend (Odoo-Test).launch    |  32 ----
         backend/OpenEMS-Backend.launch                |  36 -----
         backend/pom.xml                               |  10 --
         .../src/main/java/io/openems/backend/App.java |  97 -----------
         .../backend/core/ConnectionManager.java       |  24 ---
         .../core/ConnectionManagerSingleton.java      |  81 ----------
         .../io/openems/backend/metadata/Metadata.java |  63 --------
         .../metadata/api/MetadataSingleton.java       |  11 --
         .../dummy/MetadataDummySingleton.java         |  47 ------
         .../dummy/device/MetadataDummyDevice.java     | 105 ++++--------
         .../device/MetadataDummyDeviceModel.java      |  37 ++---
         .../metadata/file/MetadataFileSingleton.java  |  50 ------
         .../file/device/MetadataFileDevice.java       | 109 ++++---------
         .../file/device/MetadataFileDeviceModel.java  |  28 ++--
         .../backend/metadata/odoo/OdooModel.java      |  78 ---------
         .../backend/metadata/odoo/OdooObject.java     |  87 ----------
         .../backend/metadata/odoo/device/Field.java   |  15 --
         .../metadata/odoo/device/OdooDevice.java      | 150 ------------------
         .../metadata/odoo/device/OdooDeviceModel.java |  55 -------
         .../io/openems/backend/restapi/RestApi.java   |  33 ----
         .../backend/restapi/RestApiSingleton.java     |  21 ++-
         .../restapi/route/DevicesAllRestlet.java      |  25 +--
         .../dummy/TimedataDummySingleton.java         |  39 -----
         .../openems/backend/utilities/ManyToMany.java |  51 ------
         .../backend/utilities/MultiKeyMap.java        |  34 ----
         .../backend/utilities/StringUtils.java        |  15 --
         29 files changed, 142 insertions(+), 1303 deletions(-)
         delete mode 100644 backend/OpenEMS-Backend (Dummy).launch
         delete mode 100644 backend/OpenEMS-Backend (File).launch
         delete mode 100644 backend/OpenEMS-Backend (Odoo-Test).launch
         delete mode 100644 backend/OpenEMS-Backend.launch
         delete mode 100644 backend/src/main/java/io/openems/backend/App.java
         delete mode 100644 backend/src/main/java/io/openems/backend/core/ConnectionManager.java
         delete mode 100644 backend/src/main/java/io/openems/backend/core/ConnectionManagerSingleton.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/Metadata.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/api/MetadataSingleton.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/dummy/MetadataDummySingleton.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/file/MetadataFileSingleton.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/odoo/OdooModel.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/odoo/OdooObject.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/odoo/device/Field.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDevice.java
         delete mode 100644 backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDeviceModel.java
         delete mode 100644 backend/src/main/java/io/openems/backend/restapi/RestApi.java
         delete mode 100644 backend/src/main/java/io/openems/backend/timedata/dummy/TimedataDummySingleton.java
         delete mode 100644 backend/src/main/java/io/openems/backend/utilities/ManyToMany.java
         delete mode 100644 backend/src/main/java/io/openems/backend/utilities/MultiKeyMap.java
         delete mode 100644 backend/src/main/java/io/openems/backend/utilities/StringUtils.java
        
        diff --git a/backend/.classpath b/backend/.classpath
        index caa0a4ec100..393464cc051 100644
        --- a/backend/.classpath
        +++ b/backend/.classpath
        @@ -1,28 +1,27 @@
        -
        -
        -	
        -		
        -			
        -			
        -		
        -	
        -	
        -		
        -			
        -			
        -		
        -	
        -	
        -		
        -			
        -		
        -	
        -	
        -		
        -			
        -		
        -	
        -	
        -	
        -	
        -
        +
        +
        +	
        +		
        +			
        +			
        +		
        +	
        +	
        +		
        +			
        +			
        +		
        +	
        +	
        +		
        +			
        +		
        +	
        +	
        +		
        +			
        +		
        +	
        +	
        +	
        +
        diff --git a/backend/OpenEMS-Backend (Dummy).launch b/backend/OpenEMS-Backend (Dummy).launch
        deleted file mode 100644
        index 00efbb2ab5b..00000000000
        --- a/backend/OpenEMS-Backend (Dummy).launch	
        +++ /dev/null
        @@ -1,28 +0,0 @@
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        diff --git a/backend/OpenEMS-Backend (File).launch b/backend/OpenEMS-Backend (File).launch
        deleted file mode 100644
        index 9a9363070ac..00000000000
        --- a/backend/OpenEMS-Backend (File).launch	
        +++ /dev/null
        @@ -1,29 +0,0 @@
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        diff --git a/backend/OpenEMS-Backend (Odoo-Test).launch b/backend/OpenEMS-Backend (Odoo-Test).launch
        deleted file mode 100644
        index a3ef2ef056b..00000000000
        --- a/backend/OpenEMS-Backend (Odoo-Test).launch	
        +++ /dev/null
        @@ -1,32 +0,0 @@
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        diff --git a/backend/OpenEMS-Backend.launch b/backend/OpenEMS-Backend.launch
        deleted file mode 100644
        index b19ef24708e..00000000000
        --- a/backend/OpenEMS-Backend.launch
        +++ /dev/null
        @@ -1,36 +0,0 @@
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        diff --git a/backend/pom.xml b/backend/pom.xml
        index fb9117fdf71..5fe4decb9de 100644
        --- a/backend/pom.xml
        +++ b/backend/pom.xml
        @@ -35,11 +35,6 @@
         		
         	
         	
        -		
        -			io.openems
        -			common
        -			${project.version}
        -		
         		
         			com.google.code.gson
         			gson
        @@ -70,11 +65,6 @@
         			org.restlet.ext.slf4j
         			${restlet.version}
         		
        -		
        -			com.odoojava
        -			odoo-java-api
        -			${odoo-java-api.version}
        -		
         		
         			org.slf4j
         			slf4j-api
        diff --git a/backend/src/main/java/io/openems/backend/App.java b/backend/src/main/java/io/openems/backend/App.java
        deleted file mode 100644
        index 231c70a4f88..00000000000
        --- a/backend/src/main/java/io/openems/backend/App.java
        +++ /dev/null
        @@ -1,97 +0,0 @@
        -package io.openems.backend;
        -
        -import java.io.File;
        -import java.util.Optional;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import io.openems.backend.browserwebsocket.BrowserWebsocket;
        -import io.openems.backend.metadata.Metadata;
        -import io.openems.backend.openemswebsocket.OpenemsWebsocket;
        -import io.openems.backend.restapi.RestApi;
        -import io.openems.backend.timedata.Timedata;
        -import io.openems.common.exceptions.OpenemsException;
        -import io.openems.common.utils.EnvUtils;
        -
        -public class App {
        -	private static Logger log = LoggerFactory.getLogger(App.class);
        -
        -	public static void main(String[] args) {
        -		log.info("OpenEMS-Backend starting...");
        -
        -		// Configure everything
        -		try {
        -			initMetadataProvider();
        -			initTimedataProvider();
        -			initOpenemsWebsocket();
        -			initBrowserWebsocket();
        -			initRestApi();
        -
        -			log.info("OpenEMS Backend started.");
        -			log.info("================================================================================");
        -		} catch (OpenemsException e) {
        -			log.error("OpenEMS Backend failed to start: " + e.getMessage());
        -			System.exit(1);
        -		}
        -	}
        -
        -	/**
        -	 * Configures a metadata provider. It uses either Odoo as backend or a simple Dummy provider.
        -	 *
        -	 * @throws Exception
        -	 */
        -	private static void initMetadataProvider() throws OpenemsException {
        -		Optional metadataOpt = EnvUtils.getAsOptionalString("METADATA");
        -		if (metadataOpt.isPresent() && metadataOpt.get().equals("DUMMY")) {
        -			log.info("Start Dummy Metadata provider");
        -			Metadata.initializeDummy();
        -		} else if (metadataOpt.isPresent() && metadataOpt.get().equals("FILE")) {
        -			log.info("Start FILE Metadata provider");
        -			File file = new File(EnvUtils.getAsString("METADATA_FILE"));
        -			Metadata.initializeFile(file);
        -		} else {
        -			int port = EnvUtils.getAsInt("ODOO_PORT");
        -			String url = EnvUtils.getAsString("ODOO_URL");
        -			String database = EnvUtils.getAsString("ODOO_DATABASE");
        -			log.info("Connect to Odoo. Url [" + url + ":" + port + "] Database [" + database + "]");
        -			String username = EnvUtils.getAsString("ODOO_USERNAME");
        -			String password = EnvUtils.getAsString("ODOO_PASSWORD");
        -			Metadata.initializeOdoo(url, port, database, username, password);
        -		}
        -	}
        -
        -	private static void initTimedataProvider() throws OpenemsException {
        -		Optional timedataOpt = EnvUtils.getAsOptionalString("TIMEDATA");
        -		if (timedataOpt.isPresent() && timedataOpt.get().equals("DUMMY")) {
        -			log.info("Start Dummy Timedata provider");
        -			Timedata.initializeDummy();
        -		} else {
        -			int port = Integer.valueOf(System.getenv("INFLUX_PORT"));
        -			String url = EnvUtils.getAsString("INFLUX_URL");
        -			String database = EnvUtils.getAsString("INFLUX_DATABASE");
        -			log.info("Connect to InfluxDB. Url [" + url + ":" + port + "], Database [" + database + "]");
        -			String username = EnvUtils.getAsString("INFLUX_USERNAME");
        -			String password = EnvUtils.getAsString("INFLUX_PASSWORD");
        -			Timedata.initializeInfluxdb(database, url, port, username, password);
        -		}
        -	}
        -
        -	private static void initOpenemsWebsocket() throws OpenemsException {
        -		int port = EnvUtils.getAsInt("OPENEMS_WEBSOCKET_PORT");
        -		log.info("Start OpenEMS Websocket server on port [" + port + "]");
        -		OpenemsWebsocket.initialize(port);
        -	}
        -
        -	private static void initBrowserWebsocket() throws OpenemsException {
        -		int port = EnvUtils.getAsInt("BROWSER_WEBSOCKET_PORT");
        -		log.info("Start Browser Websocket server on port [" + port + "]");
        -		BrowserWebsocket.initialize(port);
        -	}
        -
        -	private static void initRestApi() throws OpenemsException {
        -		int port = EnvUtils.getAsInt("REST_API_PORT");
        -		log.info("Start Rest-Api server on port [" + port + "]");
        -		RestApi.initialize(port);
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/core/ConnectionManager.java b/backend/src/main/java/io/openems/backend/core/ConnectionManager.java
        deleted file mode 100644
        index e6f09880f3d..00000000000
        --- a/backend/src/main/java/io/openems/backend/core/ConnectionManager.java
        +++ /dev/null
        @@ -1,24 +0,0 @@
        -package io.openems.backend.core;
        -
        -/**
        - * Provider for ConnectionManager singleton
        - *
        - * @author stefan.feilmeier
        - *
        - */
        -public class ConnectionManager {
        -
        -	private static ConnectionManagerSingleton instance = null;
        -
        -	/**
        -	 * Returns the singleton instance
        -	 *
        -	 * @return
        -	 */
        -	public static synchronized ConnectionManagerSingleton instance() {
        -		if (ConnectionManager.instance == null) {
        -			ConnectionManager.instance = new ConnectionManagerSingleton();
        -		}
        -		return ConnectionManager.instance;
        -	}
        -}
        \ No newline at end of file
        diff --git a/backend/src/main/java/io/openems/backend/core/ConnectionManagerSingleton.java b/backend/src/main/java/io/openems/backend/core/ConnectionManagerSingleton.java
        deleted file mode 100644
        index 2ecfc7137c4..00000000000
        --- a/backend/src/main/java/io/openems/backend/core/ConnectionManagerSingleton.java
        +++ /dev/null
        @@ -1,81 +0,0 @@
        -package io.openems.backend.core;
        -
        -import java.util.Optional;
        -
        -import org.java_websocket.WebSocket;
        -
        -import com.google.common.collect.BiMap;
        -import com.google.common.collect.HashBiMap;
        -import com.google.common.collect.Maps;
        -
        -import io.openems.backend.browserwebsocket.session.BrowserSession;
        -import io.openems.backend.utilities.ManyToMany;
        -
        -public class ConnectionManagerSingleton {
        -
        -	protected ConnectionManagerSingleton() {};
        -
        -	/**
        -	 * Stores active websockets to browsers
        -	 */
        -	private final BiMap browserWebsockets = Maps.synchronizedBiMap(HashBiMap.create());
        -
        -	/**
        -	 * Stores active websockets to openems devices (value = deviceName, e.g. 'fems5')
        -	 */
        -	private final BiMap openemsWebsockets = Maps.synchronizedBiMap(HashBiMap.create());
        -
        -	/**
        -	 * Stores active interconnections between browser (key) and openems (value) websockets
        -	 */
        -	private final ManyToMany websocketInterconnection = new ManyToMany<>();
        -
        -	/*
        -	 * Helper methods for Browser <-> OpenEMS interconnection
        -	 */
        -	public void addWebsocketInterconnection(WebSocket browserWebsocket, WebSocket openemsWebsocket) {
        -		websocketInterconnection.put(browserWebsocket, openemsWebsocket);
        -	}
        -
        -	public void removeWebsocketInterconnection(WebSocket browserWebsocket, WebSocket openemsWebsocket) {
        -		websocketInterconnection.remove(browserWebsocket, openemsWebsocket);
        -	}
        -
        -	/*
        -	 * Helper methods for Browser websockets
        -	 */
        -	public void addBrowserWebsocket(WebSocket websocket, BrowserSession session) {
        -		this.browserWebsockets.forcePut(websocket, session);
        -	}
        -
        -	public void removeBrowserWebsocket(WebSocket websocket) {
        -		this.browserWebsockets.remove(websocket);
        -		this.websocketInterconnection.removeAllKeys(websocket);
        -	}
        -
        -	/*
        -	 * Helper methods for OpenEMS websockets
        -	 */
        -	public void addOpenemsWebsocket(WebSocket websocket, String deviceName) {
        -		this.openemsWebsockets.forcePut(websocket, deviceName);
        -	}
        -
        -	public void removeOpenemsWebsocket(WebSocket websocket) {
        -		this.openemsWebsockets.remove(websocket);
        -		this.websocketInterconnection.removeAllValues(websocket);
        -	}
        -
        -	public Optional getDeviceNameFromOpenemsWebsocket(WebSocket websocket) {
        -		String deviceName = this.openemsWebsockets.get(websocket);
        -		return Optional.ofNullable(deviceName);
        -	}
        -
        -	public Optional getOpenemsWebsocketFromDeviceName(String deviceName) {
        -		WebSocket websocket = this.openemsWebsockets.inverse().get(deviceName);
        -		return Optional.ofNullable(websocket);
        -	}
        -
        -	public boolean isOpenemsWebsocketConnected(String deviceName) {
        -		return this.openemsWebsockets.containsValue(deviceName);
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/Metadata.java b/backend/src/main/java/io/openems/backend/metadata/Metadata.java
        deleted file mode 100644
        index 2bb017be31e..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/Metadata.java
        +++ /dev/null
        @@ -1,63 +0,0 @@
        -package io.openems.backend.metadata;
        -
        -import java.io.File;
        -import java.io.IOException;
        -
        -import io.openems.backend.metadata.api.MetadataSingleton;
        -import io.openems.backend.metadata.dummy.MetadataDummySingleton;
        -import io.openems.backend.metadata.file.MetadataFileSingleton;
        -import io.openems.backend.metadata.odoo.OdooSingleton;
        -import io.openems.common.exceptions.OpenemsException;
        -
        -/**
        - * Provider for Metadata singleton
        - *
        - * @author stefan.feilmeier
        - *
        - */
        -public class Metadata {
        -
        -	private static MetadataSingleton instance = null;
        -
        -	/**
        -	 * Initialize Odoo object
        -	 *
        -	 * @param port
        -	 * @throws Exception
        -	 */
        -	public static synchronized void initializeOdoo(String url, int port, String database, String username,
        -			String password) throws OpenemsException {
        -		if (url == null || database == null || username == null || password == null) {
        -			throw new OpenemsException("Config missing: database [" + database + "], url [" + url + "], port [" + port
        -					+ "] username [" + username + "], password [" + password + "]");
        -		}
        -		Metadata.instance = new OdooSingleton(url, port, database, username, password);
        -	}
        -
        -	/**
        -	 * Initialize Dummy provider
        -	 *
        -	 * @param port
        -	 * @throws Exception
        -	 */
        -	public static synchronized void initializeDummy() {
        -		Metadata.instance = new MetadataDummySingleton();
        -	}
        -
        -	/**
        -	 * Returns the singleton instance
        -	 *
        -	 * @return
        -	 */
        -	public static synchronized MetadataSingleton instance() {
        -		return Metadata.instance;
        -	}
        -
        -	public static void initializeFile(File file) throws OpenemsException {
        -		try {
        -			Metadata.instance = new MetadataFileSingleton(file);
        -		} catch (IOException e) {
        -			throw new OpenemsException("Can not open metadata file.");
        -		}
        -	}
        -}
        \ No newline at end of file
        diff --git a/backend/src/main/java/io/openems/backend/metadata/api/MetadataSingleton.java b/backend/src/main/java/io/openems/backend/metadata/api/MetadataSingleton.java
        deleted file mode 100644
        index ad5b52af5ac..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/api/MetadataSingleton.java
        +++ /dev/null
        @@ -1,11 +0,0 @@
        -package io.openems.backend.metadata.api;
        -
        -import io.openems.backend.browserwebsocket.session.BrowserSession;
        -import io.openems.backend.metadata.api.device.MetadataDeviceModel;
        -import io.openems.common.exceptions.OpenemsException;
        -
        -public interface MetadataSingleton {
        -	public void getInfoWithSession(BrowserSession session) throws OpenemsException;
        -
        -	public MetadataDeviceModel getDeviceModel();
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/MetadataDummySingleton.java b/backend/src/main/java/io/openems/backend/metadata/dummy/MetadataDummySingleton.java
        deleted file mode 100644
        index 609d329c544..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/dummy/MetadataDummySingleton.java
        +++ /dev/null
        @@ -1,47 +0,0 @@
        -package io.openems.backend.metadata.dummy;
        -
        -import com.google.common.collect.LinkedHashMultimap;
        -
        -import io.openems.backend.browserwebsocket.session.BrowserSession;
        -import io.openems.backend.browserwebsocket.session.BrowserSessionData;
        -import io.openems.backend.metadata.api.MetadataSingleton;
        -import io.openems.backend.metadata.api.device.MetadataDeviceModel;
        -import io.openems.backend.metadata.dummy.device.MetadataDummyDeviceModel;
        -import io.openems.common.exceptions.OpenemsException;
        -import io.openems.common.session.SessionData;
        -import io.openems.common.types.DeviceImpl;
        -
        -public class MetadataDummySingleton implements MetadataSingleton {
        -	// private final Logger log = LoggerFactory.getLogger(MetadataDummySingleton.class);
        -
        -	private MetadataDummyDeviceModel deviceModel;
        -
        -	public MetadataDummySingleton() {
        -		this.deviceModel = new MetadataDummyDeviceModel();
        -	}
        -
        -	/**
        -	 * Returns static device data
        -	 *
        -	 * @return
        -	 * @throws OpenemsException
        -	 */
        -	@Override
        -	public void getInfoWithSession(BrowserSession session) throws OpenemsException {
        -		SessionData sessionData = session.getData();
        -		BrowserSessionData data = (BrowserSessionData) sessionData;
        -		data.setUserId(0);
        -		// Devices can have the same name, that's why we use a Multimap.
        -		LinkedHashMultimap deviceMap = LinkedHashMultimap.create();
        -		for (DeviceImpl device : this.deviceModel.getAllDevices()) {
        -			deviceMap.put(device.getName(), device);
        -		}
        -		data.setDevices(deviceMap);
        -		return;
        -	}
        -
        -	@Override
        -	public MetadataDeviceModel getDeviceModel() {
        -		return deviceModel;
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java
        index d13d23a648f..de7e22bd285 100644
        --- a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java
        +++ b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java
        @@ -1,17 +1,9 @@
         package io.openems.backend.metadata.dummy.device;
         
        -import java.util.Optional;
        -
         import org.slf4j.Logger;
         import org.slf4j.LoggerFactory;
         
        -import com.google.gson.JsonObject;
        -
        -import io.openems.backend.metadata.api.device.MetadataDevice;
        -import io.openems.common.exceptions.OpenemsException;
        -import io.openems.common.types.DeviceImpl;
        -
        -public class MetadataDummyDevice extends DeviceImpl implements MetadataDevice {
        +public class MetadataDummyDevice {
         
         	private final Logger log = LoggerFactory.getLogger(MetadataDummyDevice.class);
         
        @@ -19,77 +11,50 @@ public class MetadataDummyDevice extends DeviceImpl implements MetadataDevice {
         	private final String apikey;
         
         	public MetadataDummyDevice(String name, String comment, String producttype, String role, int id, String apikey) {
        -		super(name, comment, producttype, role);
         		this.id = id;
         		this.apikey = apikey;
         	}
         
        -	@Override
        -	public Optional getIdOpt() {
        -		return Optional.of(this.id);
        -	}
        -
         	public String getApikey() {
         		return apikey;
         	}
         
        -	@Override
        -	public String getName() {
        -		return super.getName();
        -	}
        -
        -	@Override
        -	public String getComment() {
        -		return super.getComment();
        -	}
        -
        -	@Override
         	public String getState() {
         		return "active";
         	}
         
        -	@Override
        -	public String getProductType() {
        -		return super.getProducttype();
        -	}
        -
        -	@Override
        -	public JsonObject getOpenemsConfig() {
        -		return new JsonObject();
        -	}
        -
        -	@Override
        -	public void setOpenemsConfig(JsonObject j) {
        -		log.info("Metadata Dummy. Would set OpenEMS config: " + j.toString());
        -	}
        -
        -	@Override
        -	public void setState(String state) {
        -		log.info("Metadata Dummy. Would set state: " + state);
        -	}
        -
        -	@Override
        -	public void setSoc(int value) {
        -		log.info("Metadata Dummy. Would set SOC: " + value);
        -	}
        -
        -	@Override
        -	public void setLastMessage() {
        -		log.info("Metadata Dummy. Would set LastMessage");
        -	}
        -
        -	@Override
        -	public void setLastUpdate() {
        -		log.debug("Metadata Dummy. Would set LastUpdate");
        -	}
        -
        -	@Override
        -	public void setIpV4(String value) {
        -		log.info("Metadata Dummy. Would set IPv4: " + value);
        -	}
        -
        -	@Override
        -	public void writeObject() throws OpenemsException {
        -		log.debug("Metadata Dummy. Would write object");
        -	}
        +	// @Override
        +	// public void setOpenemsConfig(JsonObject j) {
        +	// log.info("Metadata Dummy. Would set OpenEMS config: " + j.toString());
        +	// }
        +	//
        +	// @Override
        +	// public void setState(String state) {
        +	// log.info("Metadata Dummy. Would set state: " + state);
        +	// }
        +	//
        +	// @Override
        +	// public void setSoc(int value) {
        +	// log.info("Metadata Dummy. Would set SOC: " + value);
        +	// }
        +	//
        +	// @Override
        +	// public void setLastMessage() {
        +	// log.info("Metadata Dummy. Would set LastMessage");
        +	// }
        +	//
        +	// @Override
        +	// public void setLastUpdate() {
        +	// log.debug("Metadata Dummy. Would set LastUpdate");
        +	// }
        +	//
        +	// @Override
        +	// public void setIpV4(String value) {
        +	// log.info("Metadata Dummy. Would set IPv4: " + value);
        +	// }
        +	//
        +	// @Override
        +	// public void writeObject() throws OpenemsException {
        +	// log.debug("Metadata Dummy. Would write object");
        +	// }
         }
        diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java
        index 09d332f8ef6..806cb1adae7 100644
        --- a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java
        +++ b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java
        @@ -3,11 +3,7 @@
         import java.util.ArrayList;
         import java.util.List;
         
        -import io.openems.backend.metadata.api.device.MetadataDeviceModel;
        -import io.openems.backend.metadata.api.device.MetadataDevices;
        -import io.openems.common.exceptions.OpenemsException;
        -
        -public class MetadataDummyDeviceModel implements MetadataDeviceModel {
        +public class MetadataDummyDeviceModel {
         
         	private static int lastId = 0;
         
        @@ -15,22 +11,21 @@ public class MetadataDummyDeviceModel implements MetadataDeviceModel {
         
         	public MetadataDummyDeviceModel() {}
         
        -	@Override
        -	public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException {
        -		// filter and convert to new list
        -		MetadataDevices result = new MetadataDevices();
        -		for (MetadataDummyDevice device : this.devices) {
        -			if (device.getApikey().equals(apikey)) {
        -				result.add(device);
        -			}
        -		}
        -		// add device if it was not there yet
        -		if (result.isEmpty()) {
        -			MetadataDummyDevice device = addNewDevice(apikey);
        -			result.add(device);
        -		}
        -		return result;
        -	}
        +	// public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException {
        +	// // filter and convert to new list
        +	// MetadataDevices result = new MetadataDevices();
        +	// for (MetadataDummyDevice device : this.devices) {
        +	// if (device.getApikey().equals(apikey)) {
        +	// result.add(device);
        +	// }
        +	// }
        +	// // add device if it was not there yet
        +	// if (result.isEmpty()) {
        +	// MetadataDummyDevice device = addNewDevice(apikey);
        +	// result.add(device);
        +	// }
        +	// return result;
        +	// }
         
         	private MetadataDummyDevice addNewDevice(String apikey) {
         		int id = MetadataDummyDeviceModel.lastId++;
        diff --git a/backend/src/main/java/io/openems/backend/metadata/file/MetadataFileSingleton.java b/backend/src/main/java/io/openems/backend/metadata/file/MetadataFileSingleton.java
        deleted file mode 100644
        index 4d7f964457c..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/file/MetadataFileSingleton.java
        +++ /dev/null
        @@ -1,50 +0,0 @@
        -package io.openems.backend.metadata.file;
        -
        -import java.io.File;
        -import java.io.IOException;
        -
        -import com.google.common.collect.LinkedHashMultimap;
        -
        -import io.openems.backend.browserwebsocket.session.BrowserSession;
        -import io.openems.backend.browserwebsocket.session.BrowserSessionData;
        -import io.openems.backend.metadata.api.MetadataSingleton;
        -import io.openems.backend.metadata.api.device.MetadataDeviceModel;
        -import io.openems.backend.metadata.file.device.MetadataFileDeviceModel;
        -import io.openems.common.exceptions.OpenemsException;
        -import io.openems.common.session.SessionData;
        -import io.openems.common.types.DeviceImpl;
        -
        -public class MetadataFileSingleton implements MetadataSingleton {
        -	// private final Logger log = LoggerFactory.getLogger(MetadataDummySingleton.class);
        -
        -	private MetadataFileDeviceModel deviceModel;
        -
        -	public MetadataFileSingleton(File file) throws IOException {
        -		this.deviceModel = new MetadataFileDeviceModel(file);
        -	}
        -
        -	/**
        -	 * Returns static device data
        -	 *
        -	 * @return
        -	 * @throws OpenemsException
        -	 */
        -	@Override
        -	public void getInfoWithSession(BrowserSession session) throws OpenemsException {
        -		SessionData sessionData = session.getData();
        -		BrowserSessionData data = (BrowserSessionData) sessionData;
        -		data.setUserId(0);
        -		// Devices can have the same name, that's why we use a Multimap.
        -		LinkedHashMultimap deviceMap = LinkedHashMultimap.create();
        -		for (DeviceImpl device : this.deviceModel.getAllDevices()) {
        -			deviceMap.put(device.getName(), device);
        -		}
        -		data.setDevices(deviceMap);
        -		return;
        -	}
        -
        -	@Override
        -	public MetadataDeviceModel getDeviceModel() {
        -		return deviceModel;
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java
        index 1dc3b061077..2b0d37838d2 100644
        --- a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java
        +++ b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java
        @@ -1,17 +1,9 @@
         package io.openems.backend.metadata.file.device;
         
        -import java.util.Optional;
        -
         import org.slf4j.Logger;
         import org.slf4j.LoggerFactory;
         
        -import com.google.gson.JsonObject;
        -
        -import io.openems.backend.metadata.api.device.MetadataDevice;
        -import io.openems.common.exceptions.OpenemsException;
        -import io.openems.common.types.DeviceImpl;
        -
        -public class MetadataFileDevice extends DeviceImpl implements MetadataDevice {
        +public class MetadataFileDevice {
         
         	private final Logger log = LoggerFactory.getLogger(MetadataFileDevice.class);
         
        @@ -19,77 +11,46 @@ public class MetadataFileDevice extends DeviceImpl implements MetadataDevice {
         	private final String apikey;
         
         	public MetadataFileDevice(String name, String comment, String producttype, String role, int id, String apikey) {
        -		super(name, comment, producttype, role);
         		this.id = id;
         		this.apikey = apikey;
         	}
         
        -	@Override
        -	public Optional getIdOpt() {
        -		return Optional.of(this.id);
        -	}
        -
        -	public String getApikey() {
        -		return apikey;
        -	}
        -
        -	@Override
        -	public String getName() {
        -		return super.getName();
        -	}
        -
        -	@Override
        -	public String getComment() {
        -		return super.getComment();
        -	}
        -
        -	@Override
         	public String getState() {
         		return "active";
         	}
         
        -	@Override
        -	public String getProductType() {
        -		return super.getProducttype();
        -	}
        -
        -	@Override
        -	public JsonObject getOpenemsConfig() {
        -		return new JsonObject();
        -	}
        -
        -	@Override
        -	public void setOpenemsConfig(JsonObject j) {
        -		log.info("Metadata Dummy. Would set OpenEMS config: " + j.toString());
        -	}
        -
        -	@Override
        -	public void setState(String state) {
        -		log.info("Metadata Dummy. Would set state: " + state);
        -	}
        -
        -	@Override
        -	public void setSoc(int value) {
        -		log.info("Metadata Dummy. Would set SOC: " + value);
        -	}
        -
        -	@Override
        -	public void setLastMessage() {
        -		log.info("Metadata Dummy. Would set LastMessage");
        -	}
        -
        -	@Override
        -	public void setLastUpdate() {
        -		log.debug("Metadata Dummy. Would set LastUpdate");
        -	}
        -
        -	@Override
        -	public void setIpV4(String value) {
        -		log.info("Metadata Dummy. Would set IPv4: " + value);
        -	}
        -
        -	@Override
        -	public void writeObject() throws OpenemsException {
        -		log.debug("Metadata Dummy. Would write object");
        -	}
        +	// @Override
        +	// public void setOpenemsConfig(JsonObject j) {
        +	// log.info("Metadata Dummy. Would set OpenEMS config: " + j.toString());
        +	// }
        +	//
        +	// @Override
        +	// public void setState(String state) {
        +	// log.info("Metadata Dummy. Would set state: " + state);
        +	// }
        +	//
        +	// @Override
        +	// public void setSoc(int value) {
        +	// log.info("Metadata Dummy. Would set SOC: " + value);
        +	// }
        +	//
        +	// @Override
        +	// public void setLastMessage() {
        +	// log.info("Metadata Dummy. Would set LastMessage");
        +	// }
        +	//
        +	// @Override
        +	// public void setLastUpdate() {
        +	// log.debug("Metadata Dummy. Would set LastUpdate");
        +	// }
        +	//
        +	// @Override
        +	// public void setIpV4(String value) {
        +	// log.info("Metadata Dummy. Would set IPv4: " + value);
        +	// }
        +	//
        +	// @Override
        +	// public void writeObject() throws OpenemsException {
        +	// log.debug("Metadata Dummy. Would write object");
        +	// }
         }
        diff --git a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java
        index dc8f42e17f3..187b6beddf7 100644
        --- a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java
        +++ b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java
        @@ -7,11 +7,7 @@
         import java.util.ArrayList;
         import java.util.List;
         
        -import io.openems.backend.metadata.api.device.MetadataDeviceModel;
        -import io.openems.backend.metadata.api.device.MetadataDevices;
        -import io.openems.common.exceptions.OpenemsException;
        -
        -public class MetadataFileDeviceModel implements MetadataDeviceModel {
        +public class MetadataFileDeviceModel {
         
         	private final List devices = new ArrayList<>();
         
        @@ -28,17 +24,17 @@ public MetadataFileDeviceModel(File file) throws IOException {
         		fr.close();
         	}
         
        -	@Override
        -	public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException {
        -		// filter and convert to new list
        -		MetadataDevices result = new MetadataDevices();
        -		for (MetadataFileDevice device : this.devices) {
        -			if (device.getApikey().equals(apikey)) {
        -				result.add(device);
        -			}
        -		}
        -		return result;
        -	}
        +	// @Override
        +	// public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException {
        +	// // filter and convert to new list
        +	// MetadataDevices result = new MetadataDevices();
        +	// for (MetadataFileDevice device : this.devices) {
        +	// if (device.getApikey().equals(apikey)) {
        +	// result.add(device);
        +	// }
        +	// }
        +	// return result;
        +	// }
         
         	public List getAllDevices() {
         		return this.devices;
        diff --git a/backend/src/main/java/io/openems/backend/metadata/odoo/OdooModel.java b/backend/src/main/java/io/openems/backend/metadata/odoo/OdooModel.java
        deleted file mode 100644
        index 007e7cc9a5e..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/odoo/OdooModel.java
        +++ /dev/null
        @@ -1,78 +0,0 @@
        -package io.openems.backend.metadata.odoo;
        -
        -import java.util.List;
        -
        -import org.apache.xmlrpc.XmlRpcException;
        -
        -import com.odoojava.api.FilterCollection;
        -import com.odoojava.api.ObjectAdapter;
        -import com.odoojava.api.OdooApiException;
        -import com.odoojava.api.Row;
        -import com.odoojava.api.RowCollection;
        -import com.odoojava.api.Session;
        -
        -/**
        - * Represents an abstract model in Odoo object relational mapper
        - *
        - * @author stefan.feilmeier
        - *
        - * @param 
        - */
        -public abstract class OdooModel {
        -	private final ObjectAdapter oa;
        -
        -	/**
        -	 * Initializes the model with a Odoo session
        -	 *
        -	 * @param session
        -	 * @throws XmlRpcException
        -	 * @throws OdooApiException
        -	 */
        -	public OdooModel(Session session) throws XmlRpcException, OdooApiException {
        -		oa = session.getObjectAdapter(getModelId());
        -	}
        -
        -	/**
        -	 * Reads all objects of this model
        -	 *
        -	 * @return
        -	 * @throws XmlRpcException
        -	 * @throws OdooApiException
        -	 */
        -	public List readAllObjects() throws XmlRpcException, OdooApiException {
        -		FilterCollection filter = new FilterCollection();
        -		RowCollection rows = oa.searchAndReadObject(filter, getFields());
        -		return convertRowCollectionToList(rows);
        -	}
        -
        -	/**
        -	 * Reads all objects of this model
        -	 *
        -	 * @return
        -	 * @throws XmlRpcException
        -	 * @throws OdooApiException
        -	 */
        -	public List readObjectsWhere(String fieldName, String comparison, Object value)
        -			throws XmlRpcException, OdooApiException {
        -		FilterCollection filter = new FilterCollection();
        -		filter.add(fieldName, comparison, value);
        -		RowCollection rows = oa.searchAndReadObject(filter, getFields());
        -		return convertRowCollectionToList(rows);
        -	}
        -
        -	/**
        -	 * Converts a RowCollection to a list of POJOs
        -	 *
        -	 * @param rows
        -	 * @return
        -	 */
        -	protected abstract List convertRowCollectionToList(RowCollection rows);
        -
        -	protected void writeObject(Row row, boolean changesOnly) throws OdooApiException, XmlRpcException {
        -		oa.writeObject(row, changesOnly);
        -	}
        -
        -	protected abstract String getModelId();
        -
        -	protected abstract String[] getFields();
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/odoo/OdooObject.java b/backend/src/main/java/io/openems/backend/metadata/odoo/OdooObject.java
        deleted file mode 100644
        index 8e1c09f18b7..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/odoo/OdooObject.java
        +++ /dev/null
        @@ -1,87 +0,0 @@
        -package io.openems.backend.metadata.odoo;
        -
        -import java.time.Instant;
        -import java.time.LocalDateTime;
        -import java.time.ZoneId;
        -import java.time.ZoneOffset;
        -import java.time.ZonedDateTime;
        -import java.util.Date;
        -import java.util.Optional;
        -
        -import org.apache.xmlrpc.XmlRpcException;
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import com.odoojava.api.OdooApiException;
        -import com.odoojava.api.Row;
        -
        -import io.openems.common.exceptions.OpenemsException;
        -
        -/**
        - * Represents a record object in Odoo
        - *
        - * @author stefan.feilmeier
        - *
        - */
        -public abstract class OdooObject {
        -	private final Logger log = LoggerFactory.getLogger(OdooObject.class);
        -	private Row row;
        -	private OdooModel model;
        -	private boolean isChangedSinceLastWrite = false;
        -	private long lastWrite = 0;
        -
        -	public OdooObject(OdooModel model, Row row) {
        -		this.model = model;
        -		this.row = row;
        -	}
        -
        -	public void refreshFrom(OdooObject o) {
        -		this.row = o.row;
        -		this.model = o.model;
        -	}
        -
        -	/**
        -	 * Gets the value of the given field
        -	 *
        -	 * @param fieldName
        -	 * @return
        -	 */
        -	public Optional getOpt(String fieldName) {
        -		return Optional.ofNullable(this.row.get(fieldName));
        -	}
        -
        -	protected void put(String fieldName, Object value) {
        -		try {
        -			this.row.put(fieldName, value);
        -		} catch (OdooApiException e) {
        -			log.warn("Unable to set Odoo-value: " + e.getMessage());
        -		}
        -		isChangedSinceLastWrite = true;
        -	}
        -
        -	public void writeObject() throws OpenemsException {
        -		this.writeObject(true);
        -	}
        -
        -	public void writeObject(boolean changesOnly) throws OpenemsException {
        -		long now = System.currentTimeMillis();
        -		try {
        -			if (isChangedSinceLastWrite && now - lastWrite > 60000) {
        -				// send max once per minute
        -				this.model.writeObject(this.row, changesOnly);
        -				this.lastWrite = now;
        -			}
        -		} catch (OdooApiException | XmlRpcException e) {
        -			throw new OpenemsException("Unable to write to Odoo: " + e.getMessage());
        -		} finally {
        -			isChangedSinceLastWrite = false;
        -		}
        -	}
        -
        -	protected Date odooCompatibleNow() {
        -		Instant instant = Instant.now();
        -		int seconds = ZonedDateTime.of(LocalDateTime.ofInstant(instant, ZoneOffset.UTC), ZoneId.systemDefault())
        -				.getOffset().getTotalSeconds();
        -		return Date.from(instant.minusSeconds(seconds));
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/odoo/device/Field.java b/backend/src/main/java/io/openems/backend/metadata/odoo/device/Field.java
        deleted file mode 100644
        index b28a5a6770c..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/odoo/device/Field.java
        +++ /dev/null
        @@ -1,15 +0,0 @@
        -package io.openems.backend.metadata.odoo.device;
        -
        -public class Field {
        -	protected static final String ID = "id";
        -	protected static final String NAME = "name";
        -	protected static final String NAME_NUMBER = "name_number";
        -	protected static final String COMMENT = "comment";
        -	protected static final String SOC = "soc";
        -	protected static final String LASTMESSAGE = "lastmessage";
        -	protected static final String LASTUPDATE = "lastupdate";
        -	protected static final String IPV4 = "ipv4";
        -	protected static final String OPENEMS_CONFIG = "openems_config";
        -	protected static final String STATE = "state";
        -	protected static final String PRODUCT_TYPE = "producttype";
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDevice.java b/backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDevice.java
        deleted file mode 100644
        index 35a1245e463..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDevice.java
        +++ /dev/null
        @@ -1,150 +0,0 @@
        -package io.openems.backend.metadata.odoo.device;
        -
        -import java.util.Optional;
        -
        -import com.google.gson.Gson;
        -import com.google.gson.GsonBuilder;
        -import com.google.gson.JsonObject;
        -import com.google.gson.JsonParser;
        -import com.odoojava.api.Row;
        -
        -import io.openems.backend.metadata.api.device.MetadataDevice;
        -import io.openems.backend.metadata.odoo.OdooModel;
        -import io.openems.backend.metadata.odoo.OdooObject;
        -
        -public class OdooDevice extends OdooObject implements MetadataDevice {
        -	public OdooDevice(OdooModel model, Row row) {
        -		super(model, row);
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#getNameNumber()
        -	 */
        -	@Override
        -	public Optional getIdOpt() {
        -		Optional objOpt = this.getOpt(Field.NAME_NUMBER);
        -		if (objOpt.isPresent()) {
        -			return Optional.of((Integer) objOpt.get());
        -		} else {
        -			return Optional.empty();
        -		}
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#getName()
        -	 */
        -	@Override
        -	public String getName() {
        -		return this.getOpt(Field.NAME).orElse("UNKOWN").toString();
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#getComment()
        -	 */
        -	@Override
        -	public String getComment() {
        -		return this.getOpt(Field.COMMENT).orElse("UNKOWN").toString();
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#getState()
        -	 */
        -	@Override
        -	public String getState() {
        -		return this.getOpt(Field.STATE).orElse("UNKOWN").toString();
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#getProductType()
        -	 */
        -	@Override
        -	public String getProductType() {
        -		return this.getOpt(Field.PRODUCT_TYPE).orElse("UNKOWN").toString();
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#getOpenemsConfig()
        -	 */
        -	@Override
        -	public JsonObject getOpenemsConfig() {
        -		Optional objOpt = this.getOpt(Field.OPENEMS_CONFIG);
        -		if (objOpt.isPresent()) {
        -			return new JsonParser().parse(objOpt.get().toString()).getAsJsonObject();
        -		} else {
        -			return new JsonObject();
        -		}
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#setOpenemsConfig(com.google.gson.JsonObject)
        -	 */
        -	@Override
        -	public void setOpenemsConfig(JsonObject j) {
        -		Gson gson = new GsonBuilder().setPrettyPrinting().create();
        -		put(Field.OPENEMS_CONFIG, gson.toJson(j));
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#setState(java.lang.String)
        -	 */
        -	@Override
        -	public void setState(String active) {
        -		put(Field.STATE, active);
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#setSoc(int)
        -	 */
        -	@Override
        -	public void setSoc(int value) {
        -		put(Field.SOC, value);
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#setLastMessage()
        -	 */
        -	@Override
        -	public void setLastMessage() {
        -		put(Field.LASTMESSAGE, this.odooCompatibleNow());
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#setLastUpdate()
        -	 */
        -	@Override
        -	public void setLastUpdate() {
        -		put(Field.LASTUPDATE, this.odooCompatibleNow());
        -	}
        -
        -	/*
        -	 * (non-Javadoc)
        -	 *
        -	 * @see io.openems.backend.metadata.odoo.device.Device#setIpV4(java.lang.String)
        -	 */
        -	@Override
        -	public void setIpV4(String value) {
        -		put(Field.IPV4, value);
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDeviceModel.java b/backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDeviceModel.java
        deleted file mode 100644
        index b09e60e2f68..00000000000
        --- a/backend/src/main/java/io/openems/backend/metadata/odoo/device/OdooDeviceModel.java
        +++ /dev/null
        @@ -1,55 +0,0 @@
        -package io.openems.backend.metadata.odoo.device;
        -
        -import java.util.ArrayList;
        -import java.util.List;
        -
        -import org.apache.xmlrpc.XmlRpcException;
        -
        -import com.odoojava.api.OdooApiException;
        -import com.odoojava.api.RowCollection;
        -import com.odoojava.api.Session;
        -
        -import io.openems.backend.metadata.api.device.MetadataDeviceModel;
        -import io.openems.backend.metadata.api.device.MetadataDevices;
        -import io.openems.backend.metadata.odoo.OdooModel;
        -import io.openems.common.exceptions.OpenemsException;
        -
        -public class OdooDeviceModel extends OdooModel implements MetadataDeviceModel {
        -
        -	public OdooDeviceModel(Session session) throws XmlRpcException, OdooApiException {
        -		super(session);
        -	}
        -
        -	@Override
        -	protected String getModelId() {
        -		return "fems.device";
        -	}
        -
        -	@Override
        -	protected String[] getFields() {
        -		return new String[] { Field.NAME, Field.NAME_NUMBER, Field.COMMENT, Field.SOC, Field.LASTMESSAGE,
        -				Field.LASTUPDATE, Field.IPV4, Field.OPENEMS_CONFIG, Field.STATE, Field.PRODUCT_TYPE };
        -	}
        -
        -	@Override
        -	public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException {
        -		MetadataDevices devices = new MetadataDevices();
        -		try {
        -			for (OdooDevice device : this.readObjectsWhere("apikey", "=", apikey)) {
        -				devices.add(device);
        -			}
        -		} catch (XmlRpcException | OdooApiException e) {
        -			throw new OpenemsException("Unable to find device for apikey: " + e.getMessage());
        -		}
        -		return devices;
        -	}
        -
        -	@Override
        -	protected List convertRowCollectionToList(RowCollection rows) {
        -		List result = new ArrayList<>();
        -		rows.forEach(row -> {
        -			result.add(new OdooDevice(this, row));
        -		});
        -		return result;
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/restapi/RestApi.java b/backend/src/main/java/io/openems/backend/restapi/RestApi.java
        deleted file mode 100644
        index 62340b9200d..00000000000
        --- a/backend/src/main/java/io/openems/backend/restapi/RestApi.java
        +++ /dev/null
        @@ -1,33 +0,0 @@
        -package io.openems.backend.restapi;
        -
        -import io.openems.common.exceptions.OpenemsException;
        -
        -/**
        - * Provider for RestApiSingleton singleton
        - *
        - * @author stefan.feilmeier
        - *
        - */
        -public class RestApi {
        -
        -	private static RestApiSingleton instance;
        -
        -	/**
        -	 * Initialize and start the Websocketserver
        -	 *
        -	 * @param port
        -	 * @throws Exception
        -	 */
        -	public static synchronized void initialize(int port) throws OpenemsException {
        -		RestApi.instance = new RestApiSingleton(port);
        -	}
        -
        -	/**
        -	 * Returns the singleton instance
        -	 *
        -	 * @return
        -	 */
        -	public static synchronized RestApiSingleton instance() {
        -		return RestApi.instance;
        -	}
        -}
        \ No newline at end of file
        diff --git a/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java b/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java
        index b6ec6b33afc..bc1715fe129 100644
        --- a/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java
        +++ b/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java
        @@ -1,26 +1,23 @@
         package io.openems.backend.restapi;
         
         import org.restlet.Component;
        -import org.restlet.data.Protocol;
         import org.slf4j.Logger;
         import org.slf4j.LoggerFactory;
         
        -import io.openems.common.exceptions.OpenemsException;
        -
         public class RestApiSingleton {
         
         	private final Logger log = LoggerFactory.getLogger(RestApiSingleton.class);
         	private final Component component;
         
        -	public RestApiSingleton(int port) throws OpenemsException {
        +	public RestApiSingleton(int port) {
         		this.component = new Component();
        -		this.component.getServers().add(Protocol.HTTP, port);
        -		this.component.getDefaultHost().attach("/rest", new RestApiApplication());
        -		try {
        -			this.component.start();
        -		} catch (Exception e) {
        -			throw new OpenemsException("Starting REST-Api failed: " + e.getMessage());
        -		}
        -		log.info("REST-Api started on port [" + port + "].");
        +		// this.component.getServers().add(Protocol.HTTP, port);
        +		// this.component.getDefaultHost().attach("/rest", new RestApiApplication());
        +		// try {
        +		// this.component.start();
        +		// } catch (Exception e) {
        +		// throw new OpenemsException("Starting REST-Api failed: " + e.getMessage());
        +		// }
        +		// log.info("REST-Api started on port [" + port + "].");
         	}
         }
        diff --git a/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java b/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java
        index ba3174f9af7..ff4313e863f 100644
        --- a/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java
        +++ b/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java
        @@ -23,15 +23,6 @@
         import org.restlet.Request;
         import org.restlet.Response;
         import org.restlet.Restlet;
        -import org.restlet.data.MediaType;
        -import org.restlet.data.Method;
        -import org.restlet.representation.Representation;
        -import org.restlet.representation.StringRepresentation;
        -
        -import com.google.gson.JsonArray;
        -
        -import io.openems.backend.openemswebsocket.OpenemsWebsocket;
        -import io.openems.backend.openemswebsocket.session.OpenemsSession;
         
         public class DevicesAllRestlet extends Restlet {
         
        @@ -40,13 +31,13 @@ public void handle(Request request, Response response) {
         		super.handle(request, response);
         
         		// call handler methods
        -		if (request.getMethod().equals(Method.GET)) {
        -			JsonArray j = new JsonArray();
        -			for (OpenemsSession session : OpenemsWebsocket.instance().getSessions()) {
        -				j.add(session.getData().getDevices().toJson());
        -			}
        -			Representation entity = new StringRepresentation(j.toString(), MediaType.APPLICATION_JSON);
        -			response.setEntity(entity);
        -		}
        +		// if (request.getMethod().equals(Method.GET)) {
        +		// JsonArray j = new JsonArray();
        +		// for (OpenemsSession session : OpenemsWebsocket.instance().getSessions()) {
        +		// j.add(session.getData().getDevices().toJson());
        +		// }
        +		// Representation entity = new StringRepresentation(j.toString(), MediaType.APPLICATION_JSON);
        +		// response.setEntity(entity);
        +		// }
         	}
         }
        diff --git a/backend/src/main/java/io/openems/backend/timedata/dummy/TimedataDummySingleton.java b/backend/src/main/java/io/openems/backend/timedata/dummy/TimedataDummySingleton.java
        deleted file mode 100644
        index a736e5ffc0d..00000000000
        --- a/backend/src/main/java/io/openems/backend/timedata/dummy/TimedataDummySingleton.java
        +++ /dev/null
        @@ -1,39 +0,0 @@
        -package io.openems.backend.timedata.dummy;
        -
        -import java.time.ZonedDateTime;
        -import java.util.Optional;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import com.google.gson.JsonArray;
        -import com.google.gson.JsonObject;
        -
        -import io.openems.backend.metadata.api.device.MetadataDevices;
        -import io.openems.backend.timedata.api.TimedataSingleton;
        -import io.openems.backend.utilities.StringUtils;
        -import io.openems.common.exceptions.OpenemsException;
        -import io.openems.common.types.ChannelAddress;
        -
        -public class TimedataDummySingleton implements TimedataSingleton {
        -	private final Logger log = LoggerFactory.getLogger(TimedataDummySingleton.class);
        -
        -	@Override
        -	public void write(MetadataDevices devices, JsonObject jData) {
        -		log.debug("Timedata Dummy. Would write data: " + StringUtils.toShortString(jData, 100));
        -	}
        -
        -	@Override
        -	public JsonArray queryHistoricData(Optional deviceIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate,
        -			JsonObject channels, int resolution) throws OpenemsException {
        -		log.info("Timedata Dummy. Would query data: From [" + fromDate + "], To [" + toDate + "] Channels [" + channels
        -				+ "] Resolution [" + resolution + "]");
        -		return new JsonArray();
        -	}
        -
        -	@Override
        -	public Optional getChannelValue(int deviceId, ChannelAddress channelAddress) {
        -		log.info("Timedata Dummy has no cache...");
        -		return Optional.empty();
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/utilities/ManyToMany.java b/backend/src/main/java/io/openems/backend/utilities/ManyToMany.java
        deleted file mode 100644
        index 319bfb3840d..00000000000
        --- a/backend/src/main/java/io/openems/backend/utilities/ManyToMany.java
        +++ /dev/null
        @@ -1,51 +0,0 @@
        -package io.openems.backend.utilities;
        -
        -// Source: http://stackoverflow.com/questions/20390923/do-we-have-a-multibimap
        -import java.util.Set;
        -
        -import com.google.common.collect.HashMultimap;
        -import com.google.common.collect.SetMultimap;
        -
        -public class ManyToMany {
        -	private final SetMultimap keysToValues = HashMultimap.create();
        -
        -	private final SetMultimap valuesToKeys = HashMultimap.create();
        -
        -	public Set getValues(K key) {
        -		return keysToValues.get(key);
        -	}
        -
        -	public Set getKeys(V value) {
        -		return valuesToKeys.get(value);
        -	}
        -
        -	public boolean put(K key, V value) {
        -		return keysToValues.put(key, value) && valuesToKeys.put(value, key);
        -	}
        -
        -	public boolean putAll(K key, Iterable values) {
        -		boolean changed = false;
        -		for (V value : values) {
        -			changed = put(key, value) || changed;
        -		}
        -		return changed;
        -	}
        -
        -	public boolean remove(K key, V value) {
        -		return keysToValues.remove(key, value) && valuesToKeys.remove(value, key);
        -	}
        -
        -	public void removeAllKeys(K key) {
        -		keysToValues.get(key).forEach(value -> {
        -			valuesToKeys.removeAll(value);
        -		});
        -		keysToValues.removeAll(key);
        -	}
        -
        -	public void removeAllValues(V value) {
        -		valuesToKeys.get(value).forEach(key -> {
        -			keysToValues.removeAll(key);
        -		});
        -		valuesToKeys.removeAll(value);
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/utilities/MultiKeyMap.java b/backend/src/main/java/io/openems/backend/utilities/MultiKeyMap.java
        deleted file mode 100644
        index 752283aa345..00000000000
        --- a/backend/src/main/java/io/openems/backend/utilities/MultiKeyMap.java
        +++ /dev/null
        @@ -1,34 +0,0 @@
        -package io.openems.backend.utilities;
        -
        -import java.util.Map;
        -import java.util.concurrent.ConcurrentHashMap;
        -
        -import com.google.common.collect.BiMap;
        -import com.google.common.collect.HashBiMap;
        -import com.google.common.collect.Maps;
        -
        -public class MultiKeyMap {
        -	private Map k1Map = new ConcurrentHashMap<>();
        -	private BiMap k2Map = Maps.synchronizedBiMap(HashBiMap.create());
        -
        -	public MultiKeyMap() {}
        -
        -	public void put(K1 key1, K2 key2, V value) {
        -		k1Map.put(key1, value);
        -		k2Map.forcePut(key2, key1);
        -	}
        -
        -	public void put(K1 key1, V value) {
        -		k1Map.put(key1, value);
        -		k2Map.inverse().remove(key1);
        -	}
        -
        -	public V getWithKey1(K1 key1) {
        -		return k1Map.get(key1);
        -	}
        -
        -	public V getWithKey2(K2 key2) {
        -		K1 key1 = k2Map.get(key2);
        -		return getWithKey1(key1);
        -	}
        -}
        diff --git a/backend/src/main/java/io/openems/backend/utilities/StringUtils.java b/backend/src/main/java/io/openems/backend/utilities/StringUtils.java
        deleted file mode 100644
        index 48517f67e37..00000000000
        --- a/backend/src/main/java/io/openems/backend/utilities/StringUtils.java
        +++ /dev/null
        @@ -1,15 +0,0 @@
        -package io.openems.backend.utilities;
        -
        -import com.google.gson.JsonObject;
        -
        -public class StringUtils {
        -
        -	public static String toShortString(JsonObject j, int length) {
        -		String s = j.toString();
        -		if (s.length() > length - 3) {
        -			return s.substring(0, length - 3) + "...";
        -		} else {
        -			return s;
        -		}
        -	}
        -}
        
        From 993c07e4c5037172cb112562dd963c9a319d159b Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Sun, 25 Feb 2018 00:56:20 +0100
        Subject: [PATCH 099/156] Move exceptions and JsonUtils to Common
        
        ---
         edge/src/io/openems/api/channel/Channel.java  |  266 ++--
         .../io/openems/api/channel/ConfigChannel.java |    6 +-
         .../api/channel/FunctionalReadChannel.java    |  198 +--
         .../FunctionalReadChannelFunction.java        |   58 +-
         edge/src/io/openems/api/channel/Interval.java |  196 +--
         .../io/openems/api/channel/ReadChannel.java   |    7 +-
         .../io/openems/api/channel/WriteChannel.java  |    5 +-
         edge/src/io/openems/api/device/Device.java    |    2 +-
         edge/src/io/openems/api/doc/ChannelDoc.java   |  342 ++---
         .../api/exception/ConfigException.java        |   72 +-
         .../api/exception/InvalidValueException.java  |   66 +-
         .../exception/NotImplementedException.java    |   30 -
         .../api/exception/OpenemsException.java       |   34 -
         .../api/exception/OpenemsModbusException.java |   60 +-
         .../api/exception/ReflectionException.java    |   62 +-
         .../api/exception/WriteChannelException.java  |   64 +-
         edge/src/io/openems/api/security/User.java    |  524 ++++----
         edge/src/io/openems/core/ClassRepository.java |  570 ++++-----
         edge/src/io/openems/core/Config.java          |   11 +-
         edge/src/io/openems/core/ThingRepository.java | 1132 ++++++++---------
         .../io/openems/core/utilities/BitUtils.java   |    2 +-
         .../openems/core/utilities/ConfigUtils.java   |   15 +-
         .../core/utilities/InjectionUtils.java        |  554 ++++----
         .../io/openems/core/utilities/JsonUtils.java  |  326 -----
         .../openems/core/utilities/OpenemsTypes.java  |    2 +-
         .../core/utilities/SymmetricPower.java        |  610 ++++-----
         .../core/utilities/api/WriteJsonObject.java   |    2 +-
         .../api/modbustcp/ChannelRegisterMap.java     |    2 +-
         .../api/modbustcp/ModbusTcpApiController.java |    4 +-
         .../api/modbustcp/MyProcessImage.java         |    2 +-
         .../controller/api/modbustcp/MyRegister.java  |    4 +-
         .../api/rest/ComponentSingleton.java          |  178 +--
         .../api/rest/RestApiController.java           |    2 +-
         .../api/rest/internal/OpenemsEnroler.java     |   88 +-
         .../api/rest/route/ChannelRestlet.java        |    2 +-
         .../rest/route/UserChangePasswordRestlet.java |  199 ++-
         .../asymmetric/avoidtotaldischarge/Ess.java   |  134 +-
         .../controller/asymmetric/balancing/Ess.java  |  252 ++--
         .../asymmetric/balancingBandgap/Ess.java      |  182 +--
         .../asymmetric/balancingcurrent/Meter.java    |  138 +-
         .../controller/chargerlimitation/Ess.java     |  138 +-
         .../SupplyBusSwitchController.java            |    6 +-
         .../avoidtotaldischargesoctimeline/Ess.java   |  202 +--
         .../symmetric/balancingbandgap/Ess.java       |  130 +-
         .../symmetric/balancingcurrent/Meter.java     |  138 +-
         .../symmetric/balancingoffset/Ess.java        |  130 +-
         .../symmetric/balancingsurplus/Ess.java       |  132 +-
         .../symmetric/powerbyfrequency/Ess.java       |  114 +-
         .../TimelineChargeController.java             |    9 +-
         .../symmetric/voltagecharacteristic/Ess.java  |  134 +-
         .../impl/device/bcontrol/BControl.java        |    2 +-
         .../openems/impl/device/byd/Bem125ktla01.java |    2 +-
         .../carlogavazzi/em300series/EM300.java       |    2 +-
         .../commercial/FeneconCommercialAC.java       |    2 +-
         .../commercial/FeneconCommercialDC.java       |    2 +-
         .../impl/device/custom/riedmann/Riedmann.java |    2 +-
         .../impl/device/janitza/JanitzaUMG96RME.java  |    2 +-
         .../impl/device/kmtronic/KMTronicRelay.java   |    2 +-
         .../device/kmtronic/KMTronicRelayRev1.java    |    2 +-
         .../openems/impl/device/mini/FeneconMini.java |    2 +-
         .../impl/device/minireadonly/FeneconMini.java |    2 +-
         .../impl/device/pqplus/PqPlusUMD97.java       |    2 +-
         .../openems/impl/device/pro/FeneconPro.java   |    2 +-
         .../src/io/openems/impl/device/refu/Refu.java |    2 +-
         .../impl/device/simulator/Simulator.java      |    2 +-
         .../openems/impl/device/sma/SunnyIsland6.java |    2 +-
         .../openems/impl/device/socomec/Socomec.java  |    2 +-
         .../impl/device/socomec/SocomecB30.java       |    2 +-
         .../device/socomec/SocomecSinglePhase.java    |    2 +-
         .../io/openems/impl/device/spanner/BHKW.java  |    2 +-
         .../device/streetscooter/Streetscooter.java   |    2 +-
         .../impl/device/studer/StuderVs70.java        |    2 +-
         .../io/openems/impl/device/system/System.java |    2 +-
         .../AsymmetricSymmetricCombinationEss.java    |    2 +-
         .../device/system/esscluster/EssCluster.java  |    2 +-
         .../system/metercluster/MeterCluster.java     |    2 +-
         .../io/openems/impl/device/wago/WagoFB.java   |    2 +-
         .../fenecon/FeneconPersistence.java           |    2 +-
         .../influxdb/InfluxdbPersistence.java         |    2 +-
         .../influxdb/InfluxdbQueryWrapper.java        |  698 +++++-----
         .../impl/protocol/keba/ReceiveWorker.java     |    2 +-
         .../impl/protocol/modbus/ModbusDevice.java    |    2 +-
         .../protocol/simulator/SimulatorDevice.java   |    2 +-
         .../impl/protocol/studer/StuderBridge.java    |    2 +-
         .../impl/protocol/studer/StuderDevice.java    |    2 +-
         .../protocol/studer/StuderDeviceNature.java   |    2 +-
         .../internal/property/ReadProperty.java       |   72 +-
         .../internal/property/WriteProperty.java      |   68 +-
         .../impl/protocol/system/SystemDevice.java    |    2 +-
         .../scheduler/time/WeekTimeScheduler.java     |   11 +-
         .../PhaseRectificationActivePowerTest.java    |  172 +--
         .../supplybusswitch/SupplyBusTest.java        |  452 +++----
         .../symmetric/balancing/BalancingTest.java    |  340 ++---
         .../io/openems/common/utils/JsonUtils.java    |  421 +++---
         94 files changed, 4768 insertions(+), 5074 deletions(-)
         delete mode 100644 edge/src/io/openems/api/exception/NotImplementedException.java
         delete mode 100644 edge/src/io/openems/api/exception/OpenemsException.java
         delete mode 100644 edge/src/io/openems/core/utilities/JsonUtils.java
        
        diff --git a/edge/src/io/openems/api/channel/Channel.java b/edge/src/io/openems/api/channel/Channel.java
        index ed740d08692..e8614f6e007 100644
        --- a/edge/src/io/openems/api/channel/Channel.java
        +++ b/edge/src/io/openems/api/channel/Channel.java
        @@ -1,133 +1,133 @@
        -/*******************************************************************************
        - * 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.api.channel;
        -
        -import java.util.Set;
        -
        -import com.google.gson.JsonObject;
        -
        -import io.openems.api.doc.ChannelDoc;
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.api.thing.Thing;
        -import io.openems.common.exceptions.AccessDeniedException;
        -import io.openems.common.session.Role;
        -
        -//TODO change to generic to use Generic ChannelUpdate/ChangeListener
        -public interface Channel {
        -	public String id();
        -
        -	public Thing parent();
        -
        -	public String address();
        -
        -	/**
        -	 * Register a listener for update events on this Channel
        -	 *
        -	 * @param listeners
        -	 * @return itself
        -	 */
        -	public Channel addUpdateListener(ChannelUpdateListener... listeners);
        -
        -	/**
        -	 * Register a listener for change events on this Channel
        -	 *
        -	 * @param listeners
        -	 * @return itself
        -	 */
        -	public Channel addChangeListener(ChannelChangeListener... listeners);
        -
        -	/**
        -	 * Remove a listener for update events on this Channel
        -	 *
        -	 * @param listeners
        -	 * @return itself
        -	 */
        -	public Channel removeUpdateListener(ChannelUpdateListener... listeners);
        -
        -	/**
        -	 * Remove a listener for change events on this Channel
        -	 *
        -	 * @param listeners
        -	 * @return itself
        -	 */
        -	public Channel removeChangeListener(ChannelChangeListener... listeners);
        -
        -	/**
        -	 * Convert the channel to a JsonObject
        -	 *
        -	 * @return
        -	 * @throws NotImplementedException
        -	 */
        -	public JsonObject toJsonObject() throws NotImplementedException;
        -
        -	/**
        -	 * Returns Roles that have read access to this Channel.
        -	 *
        -	 * @return
        -	 */
        -	public Set readRoles();
        -
        -	/**
        -	 * Is the given Role allowed to read this Channel?
        -	 *
        -	 * @param role
        -	 * @return
        -	 */
        -	public boolean isReadAllowed(Role role);
        -
        -	/**
        -	 * Is the given Role allowed to read this Channel? Throws AccessDeniedException if not.
        -	 *
        -	 * @param role
        -	 */
        -	public void assertReadAllowed(Role role) throws AccessDeniedException;
        -
        -	/**
        -	 * Returns Roles that have write access to this Channel.
        -	 *
        -	 * @return
        -	 */
        -	public Set writeRoles();
        -
        -	/**
        -	 * Is the given Role allowed to write this Channel?
        -	 *
        -	 * @param role
        -	 * @return
        -	 */
        -	public boolean isWriteAllowed(Role role);
        -
        -	/**
        -	 * Is the given Role allowed to write this Channel? Throws AccessDeniedException if not.
        -	 *
        -	 * @param role
        -	 */
        -	public void assertWriteAllowed(Role role) throws AccessDeniedException;
        -
        -	/**
        -	 * Sets the Channel annotation. This method is called twice. Once after creating the Thing and after the thing was
        -	 * initialized via init()
        -	 *
        -	 * @throws OpenemsException
        -	 */
        -	public void setChannelDoc(ChannelDoc channelDoc) throws OpenemsException;
        -}
        +/*******************************************************************************
        + * 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.api.channel;
        +
        +import java.util.Set;
        +
        +import com.google.gson.JsonObject;
        +
        +import io.openems.api.doc.ChannelDoc;
        +import io.openems.api.thing.Thing;
        +import io.openems.common.exceptions.AccessDeniedException;
        +import io.openems.common.exceptions.NotImplementedException;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.session.Role;
        +
        +//TODO change to generic to use Generic ChannelUpdate/ChangeListener
        +public interface Channel {
        +	public String id();
        +
        +	public Thing parent();
        +
        +	public String address();
        +
        +	/**
        +	 * Register a listener for update events on this Channel
        +	 *
        +	 * @param listeners
        +	 * @return itself
        +	 */
        +	public Channel addUpdateListener(ChannelUpdateListener... listeners);
        +
        +	/**
        +	 * Register a listener for change events on this Channel
        +	 *
        +	 * @param listeners
        +	 * @return itself
        +	 */
        +	public Channel addChangeListener(ChannelChangeListener... listeners);
        +
        +	/**
        +	 * Remove a listener for update events on this Channel
        +	 *
        +	 * @param listeners
        +	 * @return itself
        +	 */
        +	public Channel removeUpdateListener(ChannelUpdateListener... listeners);
        +
        +	/**
        +	 * Remove a listener for change events on this Channel
        +	 *
        +	 * @param listeners
        +	 * @return itself
        +	 */
        +	public Channel removeChangeListener(ChannelChangeListener... listeners);
        +
        +	/**
        +	 * Convert the channel to a JsonObject
        +	 *
        +	 * @return
        +	 * @throws NotImplementedException
        +	 */
        +	public JsonObject toJsonObject() throws NotImplementedException;
        +
        +	/**
        +	 * Returns Roles that have read access to this Channel.
        +	 *
        +	 * @return
        +	 */
        +	public Set readRoles();
        +
        +	/**
        +	 * Is the given Role allowed to read this Channel?
        +	 *
        +	 * @param role
        +	 * @return
        +	 */
        +	public boolean isReadAllowed(Role role);
        +
        +	/**
        +	 * Is the given Role allowed to read this Channel? Throws AccessDeniedException if not.
        +	 *
        +	 * @param role
        +	 */
        +	public void assertReadAllowed(Role role) throws AccessDeniedException;
        +
        +	/**
        +	 * Returns Roles that have write access to this Channel.
        +	 *
        +	 * @return
        +	 */
        +	public Set writeRoles();
        +
        +	/**
        +	 * Is the given Role allowed to write this Channel?
        +	 *
        +	 * @param role
        +	 * @return
        +	 */
        +	public boolean isWriteAllowed(Role role);
        +
        +	/**
        +	 * Is the given Role allowed to write this Channel? Throws AccessDeniedException if not.
        +	 *
        +	 * @param role
        +	 */
        +	public void assertWriteAllowed(Role role) throws AccessDeniedException;
        +
        +	/**
        +	 * Sets the Channel annotation. This method is called twice. Once after creating the Thing and after the thing was
        +	 * initialized via init()
        +	 *
        +	 * @throws OpenemsException
        +	 */
        +	public void setChannelDoc(ChannelDoc channelDoc) throws OpenemsException;
        +}
        diff --git a/edge/src/io/openems/api/channel/ConfigChannel.java b/edge/src/io/openems/api/channel/ConfigChannel.java
        index b28bbfc80d4..5a5079eeef1 100644
        --- a/edge/src/io/openems/api/channel/ConfigChannel.java
        +++ b/edge/src/io/openems/api/channel/ConfigChannel.java
        @@ -28,11 +28,11 @@
         import com.google.gson.JsonSyntaxException;
         
         import io.openems.api.doc.ChannelDoc;
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.NotImplementedException;
         import io.openems.api.thing.Thing;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.utilities.InjectionUtils;
        -import io.openems.core.utilities.JsonUtils;
         
         public class ConfigChannel extends WriteChannel {
         
        diff --git a/edge/src/io/openems/api/channel/FunctionalReadChannel.java b/edge/src/io/openems/api/channel/FunctionalReadChannel.java
        index 97e84c3d5b9..b357814cc8f 100644
        --- a/edge/src/io/openems/api/channel/FunctionalReadChannel.java
        +++ b/edge/src/io/openems/api/channel/FunctionalReadChannel.java
        @@ -1,99 +1,99 @@
        -/*******************************************************************************
        - * 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.api.channel;
        -
        -import java.util.ArrayList;
        -import java.util.Arrays;
        -import java.util.List;
        -import java.util.Optional;
        -
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.api.thing.Thing;
        -
        -public class FunctionalReadChannel extends ReadChannel implements ChannelUpdateListener {
        -
        -	private List> channels = new ArrayList<>();
        -	private FunctionalReadChannelFunction func;
        -
        -	@SafeVarargs
        -	public FunctionalReadChannel(String id, Thing parent, FunctionalReadChannelFunction function,
        -			ReadChannel... channels) {
        -		super(id, parent);
        -		this.channels.addAll(Arrays.asList(channels));
        -		this.func = function;
        -		for (Channel c : channels) {
        -			c.addUpdateListener(this);
        -		}
        -	}
        -
        -	public FunctionalReadChannel(String id, Thing parent, FunctionalReadChannelFunction function) {
        -		super(id, parent);
        -		this.func = function;
        -		for (Channel c : channels) {
        -			c.addUpdateListener(this);
        -		}
        -	}
        -
        -	public void addChannel(ReadChannel channel) {
        -		synchronized (channels) {
        -			this.channels.add(channel);
        -			channel.addUpdateListener(this);
        -			update();
        -		}
        -	}
        -
        -	public void removeChannel(ReadChannel channel) {
        -		synchronized (this.channels) {
        -			channel.removeUpdateListener(this);
        -			this.channels.remove(channel);
        -			update();
        -		}
        -	}
        -
        -	@Override
        -	public void channelUpdated(Channel channel, Optional newValue) {
        -		update();
        -	}
        -
        -	private void update() {
        -		synchronized (this.channels) {
        -			@SuppressWarnings("unchecked") ReadChannel[] channels = new ReadChannel[this.channels.size()];
        -			this.channels.toArray(channels);
        -			try {
        -				updateValue(func.handle(channels));
        -			} catch (InvalidValueException e) {
        -				updateValue(null);
        -			}
        -		}
        -	}
        -
        -	@Override
        -	public FunctionalReadChannel label(T value, String label) {
        -		super.label(value, label);
        -		return this;
        -	}
        -
        -	@Override
        -	public FunctionalReadChannel unit(String unit) {
        -		super.unit(unit);
        -		return this;
        -	}
        -}
        +/*******************************************************************************
        + * 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.api.channel;
        +
        +import java.util.ArrayList;
        +import java.util.Arrays;
        +import java.util.List;
        +import java.util.Optional;
        +
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.api.thing.Thing;
        +
        +public class FunctionalReadChannel extends ReadChannel implements ChannelUpdateListener {
        +
        +	private List> channels = new ArrayList<>();
        +	private FunctionalReadChannelFunction func;
        +
        +	@SafeVarargs
        +	public FunctionalReadChannel(String id, Thing parent, FunctionalReadChannelFunction function,
        +			ReadChannel... channels) {
        +		super(id, parent);
        +		this.channels.addAll(Arrays.asList(channels));
        +		this.func = function;
        +		for (Channel c : channels) {
        +			c.addUpdateListener(this);
        +		}
        +	}
        +
        +	public FunctionalReadChannel(String id, Thing parent, FunctionalReadChannelFunction function) {
        +		super(id, parent);
        +		this.func = function;
        +		for (Channel c : channels) {
        +			c.addUpdateListener(this);
        +		}
        +	}
        +
        +	public void addChannel(ReadChannel channel) {
        +		synchronized (channels) {
        +			this.channels.add(channel);
        +			channel.addUpdateListener(this);
        +			update();
        +		}
        +	}
        +
        +	public void removeChannel(ReadChannel channel) {
        +		synchronized (this.channels) {
        +			channel.removeUpdateListener(this);
        +			this.channels.remove(channel);
        +			update();
        +		}
        +	}
        +
        +	@Override
        +	public void channelUpdated(Channel channel, Optional newValue) {
        +		update();
        +	}
        +
        +	private void update() {
        +		synchronized (this.channels) {
        +			@SuppressWarnings("unchecked") ReadChannel[] channels = new ReadChannel[this.channels.size()];
        +			this.channels.toArray(channels);
        +			try {
        +				updateValue(func.handle(channels));
        +			} catch (InvalidValueException e) {
        +				updateValue(null);
        +			}
        +		}
        +	}
        +
        +	@Override
        +	public FunctionalReadChannel label(T value, String label) {
        +		super.label(value, label);
        +		return this;
        +	}
        +
        +	@Override
        +	public FunctionalReadChannel unit(String unit) {
        +		super.unit(unit);
        +		return this;
        +	}
        +}
        diff --git a/edge/src/io/openems/api/channel/FunctionalReadChannelFunction.java b/edge/src/io/openems/api/channel/FunctionalReadChannelFunction.java
        index 09ac0b639fe..f371eb8435b 100644
        --- a/edge/src/io/openems/api/channel/FunctionalReadChannelFunction.java
        +++ b/edge/src/io/openems/api/channel/FunctionalReadChannelFunction.java
        @@ -1,29 +1,29 @@
        -/*******************************************************************************
        - * 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.api.channel;
        -
        -import io.openems.api.exception.InvalidValueException;
        -
        -public interface FunctionalReadChannelFunction {
        -
        -	@SuppressWarnings("unchecked")
        -	public T handle(ReadChannel... channels) throws InvalidValueException;
        -}
        +/*******************************************************************************
        + * 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.api.channel;
        +
        +import io.openems.api.exception.InvalidValueException;
        +
        +public interface FunctionalReadChannelFunction {
        +
        +	@SuppressWarnings("unchecked")
        +	public T handle(ReadChannel... channels) throws InvalidValueException;
        +}
        diff --git a/edge/src/io/openems/api/channel/Interval.java b/edge/src/io/openems/api/channel/Interval.java
        index 0d722394cf2..f3ed55a9cc3 100644
        --- a/edge/src/io/openems/api/channel/Interval.java
        +++ b/edge/src/io/openems/api/channel/Interval.java
        @@ -1,98 +1,98 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.channel;
        -
        -import java.util.Optional;
        -
        -import io.openems.api.exception.InvalidValueException;
        -
        -public class Interval {
        -	private Optional min = Optional.empty();
        -	private Optional max = Optional.empty();
        -
        -	public Interval() {
        -	}
        -
        -	public Interval(Optional min, Optional max) {
        -		this.min = min;
        -		this.max = max;
        -	}
        -
        -	public Interval(T min, T max) {
        -		this.min = Optional.ofNullable(min);
        -		this.max = Optional.ofNullable(max);
        -	}
        -
        -	public void min(T value) {
        -		this.min = Optional.ofNullable(value);
        -	}
        -
        -	public void max(T value) {
        -		this.max = Optional.ofNullable(value);
        -	}
        -
        -	public T max() throws InvalidValueException {
        -		return max.orElseThrow(() -> new InvalidValueException("No Max-Value available."));
        -	}
        -
        -	public Optional maxOptional() {
        -		return max;
        -	}
        -
        -	public T min() throws InvalidValueException {
        -		return min.orElseThrow(() -> new InvalidValueException("No Min-Value available."));
        -	}
        -
        -	public Optional minOptional() {
        -		return min;
        -	}
        -
        -	public boolean isPresent() {
        -		return min.isPresent() && max.isPresent();
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public boolean isAboveMin(T value) {
        -		if (min.isPresent() && min.get() instanceof Comparable) {
        -			return ((Comparable) value).compareTo(min.get()) >= 0;
        -		} else {
        -			return true;
        -		}
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public boolean isBelowMax(T value) {
        -		if (max.isPresent() && max.get() instanceof Comparable) {
        -			return ((Comparable) value).compareTo(max.get()) <= 0;
        -		} else {
        -			return true;
        -		}
        -	}
        -
        -	public boolean isWithinInterval(T value) {
        -		return isAboveMin(value) && isBelowMax(value);
        -	}
        -
        -	public void reset() {
        -		min = Optional.empty();
        -		max = Optional.empty();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.channel;
        +
        +import java.util.Optional;
        +
        +import io.openems.api.exception.InvalidValueException;
        +
        +public class Interval {
        +	private Optional min = Optional.empty();
        +	private Optional max = Optional.empty();
        +
        +	public Interval() {
        +	}
        +
        +	public Interval(Optional min, Optional max) {
        +		this.min = min;
        +		this.max = max;
        +	}
        +
        +	public Interval(T min, T max) {
        +		this.min = Optional.ofNullable(min);
        +		this.max = Optional.ofNullable(max);
        +	}
        +
        +	public void min(T value) {
        +		this.min = Optional.ofNullable(value);
        +	}
        +
        +	public void max(T value) {
        +		this.max = Optional.ofNullable(value);
        +	}
        +
        +	public T max() throws InvalidValueException {
        +		return max.orElseThrow(() -> new InvalidValueException("No Max-Value available."));
        +	}
        +
        +	public Optional maxOptional() {
        +		return max;
        +	}
        +
        +	public T min() throws InvalidValueException {
        +		return min.orElseThrow(() -> new InvalidValueException("No Min-Value available."));
        +	}
        +
        +	public Optional minOptional() {
        +		return min;
        +	}
        +
        +	public boolean isPresent() {
        +		return min.isPresent() && max.isPresent();
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public boolean isAboveMin(T value) {
        +		if (min.isPresent() && min.get() instanceof Comparable) {
        +			return ((Comparable) value).compareTo(min.get()) >= 0;
        +		} else {
        +			return true;
        +		}
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public boolean isBelowMax(T value) {
        +		if (max.isPresent() && max.get() instanceof Comparable) {
        +			return ((Comparable) value).compareTo(max.get()) <= 0;
        +		} else {
        +			return true;
        +		}
        +	}
        +
        +	public boolean isWithinInterval(T value) {
        +		return isAboveMin(value) && isBelowMax(value);
        +	}
        +
        +	public void reset() {
        +		min = Optional.empty();
        +		max = Optional.empty();
        +	}
        +}
        diff --git a/edge/src/io/openems/api/channel/ReadChannel.java b/edge/src/io/openems/api/channel/ReadChannel.java
        index b9bf6cb07b2..8583b956ab6 100644
        --- a/edge/src/io/openems/api/channel/ReadChannel.java
        +++ b/edge/src/io/openems/api/channel/ReadChannel.java
        @@ -35,15 +35,16 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelDoc;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.api.exception.OpenemsException;
         import io.openems.api.thing.Thing;
         import io.openems.common.exceptions.AccessDeniedException;
        +import io.openems.common.exceptions.NotImplementedException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.session.Role;
         import io.openems.common.types.ChannelAddress;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.Databus;
         import io.openems.core.utilities.InjectionUtils;
        -import io.openems.core.utilities.JsonUtils;
        +
         
         public class ReadChannel implements Channel, Comparable> {
         	protected final Logger log;
        diff --git a/edge/src/io/openems/api/channel/WriteChannel.java b/edge/src/io/openems/api/channel/WriteChannel.java
        index 55846a66604..d4dad7f2e9d 100644
        --- a/edge/src/io/openems/api/channel/WriteChannel.java
        +++ b/edge/src/io/openems/api/channel/WriteChannel.java
        @@ -27,12 +27,13 @@
         
         import io.openems.api.controller.Controller;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.api.exception.NotImplementedException;
         import io.openems.api.exception.WriteChannelException;
         import io.openems.api.thing.Thing;
         import io.openems.common.exceptions.AccessDeniedException;
        +import io.openems.common.exceptions.NotImplementedException;
         import io.openems.common.session.Role;
        -import io.openems.core.utilities.JsonUtils;
        +import io.openems.common.utils.JsonUtils;
        +
         
         public class WriteChannel extends ReadChannel {
         
        diff --git a/edge/src/io/openems/api/device/Device.java b/edge/src/io/openems/api/device/Device.java
        index 55a586d2ed6..70cff5da0f8 100644
        --- a/edge/src/io/openems/api/device/Device.java
        +++ b/edge/src/io/openems/api/device/Device.java
        @@ -35,8 +35,8 @@
         import io.openems.api.channel.ChannelChangeListener;
         import io.openems.api.channel.thingstate.ThingStateChannels;
         import io.openems.api.device.nature.DeviceNature;
        -import io.openems.api.exception.OpenemsException;
         import io.openems.api.thing.Thing;
        +import io.openems.common.exceptions.OpenemsException;
         
         public abstract class Device implements Thing, ChannelChangeListener {
         	public final static String THINGID_PREFIX = "_device";
        diff --git a/edge/src/io/openems/api/doc/ChannelDoc.java b/edge/src/io/openems/api/doc/ChannelDoc.java
        index b315dfbc0f9..cb7240bb227 100644
        --- a/edge/src/io/openems/api/doc/ChannelDoc.java
        +++ b/edge/src/io/openems/api/doc/ChannelDoc.java
        @@ -1,171 +1,171 @@
        -/*******************************************************************************
        - * 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.api.doc;
        -
        -import java.lang.reflect.Member;
        -import java.util.Optional;
        -import java.util.Set;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import com.google.common.collect.Sets;
        -import com.google.gson.JsonArray;
        -import com.google.gson.JsonObject;
        -
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.DeviceNature;
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.common.session.Role;
        -import io.openems.core.utilities.BitUtils;
        -import io.openems.core.utilities.InjectionUtils;
        -
        -public class ChannelDoc {
        -	private final Logger log = LoggerFactory.getLogger(ChannelDoc.class);
        -
        -	private final Member member;
        -	private final String name;
        -	private final String title;
        -	private final String description;
        -	private final Optional> typeOpt;
        -	// number of bits required for this datatype
        -	private final Optional bitLengthOpt;
        -	private final boolean optional;
        -	private final boolean array;
        -	private final Set readRoles;
        -	private final Set writeRoles;
        -	private final String defaultValue;
        -
        -	public ChannelDoc(Member member, String name, Optional channelInfoOpt) {
        -		this.member = member;
        -		this.name = name;
        -		if (channelInfoOpt.isPresent()) {
        -			ChannelInfo channelInfo = channelInfoOpt.get();
        -			this.title = channelInfo.title();
        -			this.description = channelInfo.description();
        -			this.typeOpt = Optional.of(channelInfo.type());
        -			Integer bitLength = null;
        -			try {
        -				bitLength = BitUtils.getBitLength(channelInfo.type());
        -			} catch (NotImplementedException e) {
        -				log.warn("Unable to get BitLength for Channel [" + name + "]: " + e.getMessage());
        -			}
        -			this.bitLengthOpt = Optional.ofNullable(bitLength);
        -			this.optional = channelInfo.isOptional();
        -			this.array = channelInfo.isArray();
        -			this.readRoles = Sets.newHashSet(channelInfo.readRoles());
        -			this.readRoles.add(Role.ADMIN); // ADMIN is always allowed to read
        -			this.writeRoles = Sets.newHashSet(channelInfo.writeRoles());
        -			this.writeRoles.add(Role.ADMIN);
        -			this.defaultValue = channelInfo.defaultValue();
        -		} else {
        -			this.title = ChannelInfo.DEFAULT_TITLE;
        -			this.description = ChannelInfo.DEFAULT_DESCRIPTION;
        -			this.typeOpt = Optional.empty();
        -			this.bitLengthOpt = Optional.empty();
        -			this.optional = ChannelInfo.DEFAULT_IS_OPTIONAL;
        -			this.array = ChannelInfo.DEFAULT_IS_ARRAY;
        -			this.readRoles = ChannelInfo.DEFAULT_READ_ROLES;
        -			this.writeRoles = ChannelInfo.DEFAULT_WRITE_ROLES;
        -			this.defaultValue = ChannelInfo.DEFAULT_VALUE;
        -		}
        -	}
        -
        -	public Member getMember() {
        -		return member;
        -	}
        -
        -	public String getName() {
        -		return name;
        -	}
        -
        -	public String getDescription() {
        -		return description;
        -	}
        -
        -	public Optional> getTypeOpt() {
        -		return typeOpt;
        -	}
        -
        -	public Optional getBitLengthOpt() {
        -		return bitLengthOpt;
        -	}
        -
        -	public boolean isOptional() {
        -		return optional;
        -	}
        -
        -	public String getDefaultValue() {
        -		return defaultValue;
        -	}
        -
        -	public Set getReadRoles() {
        -		return readRoles;
        -	}
        -
        -	public Set getWriteRoles() {
        -		return writeRoles;
        -	}
        -
        -	public JsonObject getAsJsonObject() {
        -		JsonObject j = new JsonObject();
        -		j.addProperty("name", this.name);
        -		j.addProperty("title", this.title);
        -		j.addProperty("description", this.description);
        -		if (typeOpt.isPresent()) {
        -			Class type = this.typeOpt.get();
        -			if (ThingMap.class.isAssignableFrom(type)) {
        -				// for ThingMap type: get the types from annotation and return JsonArray
        -				IsThingMap isThingMapAnnotation = type.getAnnotation(IsThingMap.class);
        -				j.add("type", InjectionUtils.getImplementsAsJson(isThingMapAnnotation.type()));
        -			} else if (DeviceNature.class.isAssignableFrom(type)) {
        -				// for DeviceNatures add complete class name
        -				j.addProperty("type", type.getCanonicalName());
        -			} else {
        -				// for simple types, use only simple name (e.g. 'Long', 'Integer',...)
        -				j.addProperty("type", type.getSimpleName());
        -			}
        -		}
        -		j.addProperty("optional", this.optional);
        -		j.addProperty("array", this.array);
        -		JsonArray jReadRoles = new JsonArray();
        -		for (Role role : this.readRoles) {
        -			jReadRoles.add(role.name().toLowerCase());
        -		}
        -		j.add("readRoles", jReadRoles);
        -		JsonArray jWriteRoles = new JsonArray();
        -		for (Role role : this.writeRoles) {
        -			jWriteRoles.add(role.name().toLowerCase());
        -		}
        -		j.add("writeRoles", jWriteRoles);
        -		j.addProperty("defaultValue", this.defaultValue);
        -		return j;
        -	}
        -
        -	@Override
        -	public String toString() {
        -		return "ChannelDoc [field/method=" + member.getName() + ", name=" + name + ", title=" + title
        -				+ ", description=" + description + ", type=" + typeOpt + ", bitLength=" + bitLengthOpt
        -				+ ", optional=" + optional + ", array=" + array + ", readRoles=" + readRoles + ", writeRoles="
        -				+ writeRoles + ", defaultValue=" + defaultValue + "]";
        -	}
        -}
        +/*******************************************************************************
        + * 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.api.doc;
        +
        +import java.lang.reflect.Member;
        +import java.util.Optional;
        +import java.util.Set;
        +
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import com.google.common.collect.Sets;
        +import com.google.gson.JsonArray;
        +import com.google.gson.JsonObject;
        +
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.DeviceNature;
        +import io.openems.common.exceptions.NotImplementedException;
        +import io.openems.common.session.Role;
        +import io.openems.core.utilities.BitUtils;
        +import io.openems.core.utilities.InjectionUtils;
        +
        +public class ChannelDoc {
        +	private final Logger log = LoggerFactory.getLogger(ChannelDoc.class);
        +
        +	private final Member member;
        +	private final String name;
        +	private final String title;
        +	private final String description;
        +	private final Optional> typeOpt;
        +	// number of bits required for this datatype
        +	private final Optional bitLengthOpt;
        +	private final boolean optional;
        +	private final boolean array;
        +	private final Set readRoles;
        +	private final Set writeRoles;
        +	private final String defaultValue;
        +
        +	public ChannelDoc(Member member, String name, Optional channelInfoOpt) {
        +		this.member = member;
        +		this.name = name;
        +		if (channelInfoOpt.isPresent()) {
        +			ChannelInfo channelInfo = channelInfoOpt.get();
        +			this.title = channelInfo.title();
        +			this.description = channelInfo.description();
        +			this.typeOpt = Optional.of(channelInfo.type());
        +			Integer bitLength = null;
        +			try {
        +				bitLength = BitUtils.getBitLength(channelInfo.type());
        +			} catch (NotImplementedException e) {
        +				log.warn("Unable to get BitLength for Channel [" + name + "]: " + e.getMessage());
        +			}
        +			this.bitLengthOpt = Optional.ofNullable(bitLength);
        +			this.optional = channelInfo.isOptional();
        +			this.array = channelInfo.isArray();
        +			this.readRoles = Sets.newHashSet(channelInfo.readRoles());
        +			this.readRoles.add(Role.ADMIN); // ADMIN is always allowed to read
        +			this.writeRoles = Sets.newHashSet(channelInfo.writeRoles());
        +			this.writeRoles.add(Role.ADMIN);
        +			this.defaultValue = channelInfo.defaultValue();
        +		} else {
        +			this.title = ChannelInfo.DEFAULT_TITLE;
        +			this.description = ChannelInfo.DEFAULT_DESCRIPTION;
        +			this.typeOpt = Optional.empty();
        +			this.bitLengthOpt = Optional.empty();
        +			this.optional = ChannelInfo.DEFAULT_IS_OPTIONAL;
        +			this.array = ChannelInfo.DEFAULT_IS_ARRAY;
        +			this.readRoles = ChannelInfo.DEFAULT_READ_ROLES;
        +			this.writeRoles = ChannelInfo.DEFAULT_WRITE_ROLES;
        +			this.defaultValue = ChannelInfo.DEFAULT_VALUE;
        +		}
        +	}
        +
        +	public Member getMember() {
        +		return member;
        +	}
        +
        +	public String getName() {
        +		return name;
        +	}
        +
        +	public String getDescription() {
        +		return description;
        +	}
        +
        +	public Optional> getTypeOpt() {
        +		return typeOpt;
        +	}
        +
        +	public Optional getBitLengthOpt() {
        +		return bitLengthOpt;
        +	}
        +
        +	public boolean isOptional() {
        +		return optional;
        +	}
        +
        +	public String getDefaultValue() {
        +		return defaultValue;
        +	}
        +
        +	public Set getReadRoles() {
        +		return readRoles;
        +	}
        +
        +	public Set getWriteRoles() {
        +		return writeRoles;
        +	}
        +
        +	public JsonObject getAsJsonObject() {
        +		JsonObject j = new JsonObject();
        +		j.addProperty("name", this.name);
        +		j.addProperty("title", this.title);
        +		j.addProperty("description", this.description);
        +		if (typeOpt.isPresent()) {
        +			Class type = this.typeOpt.get();
        +			if (ThingMap.class.isAssignableFrom(type)) {
        +				// for ThingMap type: get the types from annotation and return JsonArray
        +				IsThingMap isThingMapAnnotation = type.getAnnotation(IsThingMap.class);
        +				j.add("type", InjectionUtils.getImplementsAsJson(isThingMapAnnotation.type()));
        +			} else if (DeviceNature.class.isAssignableFrom(type)) {
        +				// for DeviceNatures add complete class name
        +				j.addProperty("type", type.getCanonicalName());
        +			} else {
        +				// for simple types, use only simple name (e.g. 'Long', 'Integer',...)
        +				j.addProperty("type", type.getSimpleName());
        +			}
        +		}
        +		j.addProperty("optional", this.optional);
        +		j.addProperty("array", this.array);
        +		JsonArray jReadRoles = new JsonArray();
        +		for (Role role : this.readRoles) {
        +			jReadRoles.add(role.name().toLowerCase());
        +		}
        +		j.add("readRoles", jReadRoles);
        +		JsonArray jWriteRoles = new JsonArray();
        +		for (Role role : this.writeRoles) {
        +			jWriteRoles.add(role.name().toLowerCase());
        +		}
        +		j.add("writeRoles", jWriteRoles);
        +		j.addProperty("defaultValue", this.defaultValue);
        +		return j;
        +	}
        +
        +	@Override
        +	public String toString() {
        +		return "ChannelDoc [field/method=" + member.getName() + ", name=" + name + ", title=" + title
        +				+ ", description=" + description + ", type=" + typeOpt + ", bitLength=" + bitLengthOpt
        +				+ ", optional=" + optional + ", array=" + array + ", readRoles=" + readRoles + ", writeRoles="
        +				+ writeRoles + ", defaultValue=" + defaultValue + "]";
        +	}
        +}
        diff --git a/edge/src/io/openems/api/exception/ConfigException.java b/edge/src/io/openems/api/exception/ConfigException.java
        index f6a2481b284..5873dc4f319 100644
        --- a/edge/src/io/openems/api/exception/ConfigException.java
        +++ b/edge/src/io/openems/api/exception/ConfigException.java
        @@ -1,35 +1,37 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class ConfigException extends OpenemsException {
        -
        -	private static final long serialVersionUID = 4964780198803809683L;
        -
        -	public ConfigException(String message) {
        -		super(message);
        -	}
        -	
        -	public ConfigException(String message, Throwable cause) {
        -		super(message, cause);
        -	}
        -
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.exception;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +
        +public class ConfigException extends OpenemsException {
        +
        +	private static final long serialVersionUID = 4964780198803809683L;
        +
        +	public ConfigException(String message) {
        +		super(message);
        +	}
        +
        +	public ConfigException(String message, Throwable cause) {
        +		super(message, cause);
        +	}
        +
        +}
        diff --git a/edge/src/io/openems/api/exception/InvalidValueException.java b/edge/src/io/openems/api/exception/InvalidValueException.java
        index 7075f7dba50..58434b65f29 100644
        --- a/edge/src/io/openems/api/exception/InvalidValueException.java
        +++ b/edge/src/io/openems/api/exception/InvalidValueException.java
        @@ -1,32 +1,34 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class InvalidValueException extends OpenemsException {
        -	/**
        -	 *
        -	 */
        -	private static final long serialVersionUID = -5227718238404786749L;
        -
        -	public InvalidValueException(String message) {
        -		super(message);
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.exception;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +
        +public class InvalidValueException extends OpenemsException {
        +	/**
        +	 *
        +	 */
        +	private static final long serialVersionUID = -5227718238404786749L;
        +
        +	public InvalidValueException(String message) {
        +		super(message);
        +	}
        +}
        diff --git a/edge/src/io/openems/api/exception/NotImplementedException.java b/edge/src/io/openems/api/exception/NotImplementedException.java
        deleted file mode 100644
        index 5991fd1a8fa..00000000000
        --- a/edge/src/io/openems/api/exception/NotImplementedException.java
        +++ /dev/null
        @@ -1,30 +0,0 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class NotImplementedException extends OpenemsException {
        -
        -	private static final long serialVersionUID = 1L;
        -
        -	public NotImplementedException(String message) {
        -		super(message);
        -	}
        -}
        diff --git a/edge/src/io/openems/api/exception/OpenemsException.java b/edge/src/io/openems/api/exception/OpenemsException.java
        deleted file mode 100644
        index bcd3eb435f5..00000000000
        --- a/edge/src/io/openems/api/exception/OpenemsException.java
        +++ /dev/null
        @@ -1,34 +0,0 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class OpenemsException extends io.openems.common.exceptions.OpenemsException {
        -
        -	private static final long serialVersionUID = 2244921342821407476L;
        -
        -	public OpenemsException(String message) {
        -		super(message);
        -	}
        -
        -	public OpenemsException(String message, Throwable cause) {
        -		super(message, cause);
        -	}
        -}
        diff --git a/edge/src/io/openems/api/exception/OpenemsModbusException.java b/edge/src/io/openems/api/exception/OpenemsModbusException.java
        index 9b8c016a6ee..08b0bc5483a 100644
        --- a/edge/src/io/openems/api/exception/OpenemsModbusException.java
        +++ b/edge/src/io/openems/api/exception/OpenemsModbusException.java
        @@ -1,29 +1,31 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class OpenemsModbusException extends OpenemsException {
        -	private static final long serialVersionUID = 3297320111906888878L;
        -
        -	public OpenemsModbusException(String message) {
        -		super(message);
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.exception;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +
        +public class OpenemsModbusException extends OpenemsException {
        +	private static final long serialVersionUID = 3297320111906888878L;
        +
        +	public OpenemsModbusException(String message) {
        +		super(message);
        +	}
        +}
        diff --git a/edge/src/io/openems/api/exception/ReflectionException.java b/edge/src/io/openems/api/exception/ReflectionException.java
        index 49924f88e45..74f06d62359 100644
        --- a/edge/src/io/openems/api/exception/ReflectionException.java
        +++ b/edge/src/io/openems/api/exception/ReflectionException.java
        @@ -1,30 +1,32 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class ReflectionException extends OpenemsException {
        -
        -	private static final long serialVersionUID = 4327222554707114879L;
        -
        -	public ReflectionException(String message) {
        -		super(message);
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.exception;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +
        +public class ReflectionException extends OpenemsException {
        +
        +	private static final long serialVersionUID = 4327222554707114879L;
        +
        +	public ReflectionException(String message) {
        +		super(message);
        +	}
        +}
        diff --git a/edge/src/io/openems/api/exception/WriteChannelException.java b/edge/src/io/openems/api/exception/WriteChannelException.java
        index 1c77afd9e55..a2ba4258a8d 100644
        --- a/edge/src/io/openems/api/exception/WriteChannelException.java
        +++ b/edge/src/io/openems/api/exception/WriteChannelException.java
        @@ -1,31 +1,33 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.exception;
        -
        -public class WriteChannelException extends OpenemsException {
        -
        -	private static final long serialVersionUID = 8592882509644913998L;
        -
        -	public WriteChannelException(String message) {
        -		super(message);
        -	}
        -
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.exception;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +
        +public class WriteChannelException extends OpenemsException {
        +
        +	private static final long serialVersionUID = 8592882509644913998L;
        +
        +	public WriteChannelException(String message) {
        +		super(message);
        +	}
        +
        +}
        diff --git a/edge/src/io/openems/api/security/User.java b/edge/src/io/openems/api/security/User.java
        index 5afe356238b..07c0ecc31e0 100644
        --- a/edge/src/io/openems/api/security/User.java
        +++ b/edge/src/io/openems/api/security/User.java
        @@ -1,262 +1,262 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.security;
        -
        -import java.nio.charset.StandardCharsets;
        -import java.security.NoSuchAlgorithmException;
        -import java.security.SecureRandom;
        -import java.security.spec.InvalidKeySpecException;
        -import java.util.Arrays;
        -import java.util.Base64;
        -import java.util.Base64.Decoder;
        -import java.util.Optional;
        -
        -import javax.crypto.SecretKey;
        -import javax.crypto.SecretKeyFactory;
        -import javax.crypto.spec.PBEKeySpec;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.common.session.Role;
        -import io.openems.common.utils.SecureRandomSingleton;
        -import io.openems.core.Config;
        -
        -public enum User {
        -	/*
        -	 * "GUEST" generally has readonly access
        -	 *
        -	 * default: guest/guest
        -	 */
        -	GUEST( //
        -			Role.GUEST,
        -			new byte[] { 33, -62, 51, 37, 35, -81, 52, -51, 79, -67, 15, 47, -25, 42, 69, -68, -6, 19, 103, 33, -16,
        -					-36, -87, -24, 111, -20, -30, -19, -33, -106, -78, -107 }, //
        -			"user".getBytes(StandardCharsets.ISO_8859_1) //
        -	), //
        -
        -	/*
        -	 * "OWNER" is the owner of the system.
        -	 *
        -	 * default: owner/owner
        -	 */
        -	OWNER( //
        -			Role.OWNER,
        -			new byte[] { 120, -104, 11, 5, -15, -45, -103, -24, 111, -31, 45, 112, -122, -57, -29, 120, 77, -22, -36, 2,
        -					102, 36, 32, 90, 109, 94, 125, 99, -82, 94, -95, -126 }, //
        -			"owner".getBytes(StandardCharsets.ISO_8859_1) //
        -	), //
        -	/*
        -	 * "INSTALLER" is a qualified electrician with extended configuration access
        -	 *
        -	 * default: installer/installer
        -	 */
        -	INSTALLER( //
        -			Role.INSTALLER,
        -			new byte[] { -40, -19, 93, 50, 91, 5, 119, 6, -97, -53, -97, 30, -122, -76, -2, 95, -19, 2, 17, 102, -128,
        -					-104, 20, 90, 119, -110, 69, 109, 50, -15, -3, 106 }, //
        -			"installer".getBytes(StandardCharsets.ISO_8859_1) //
        -	), //
        -	/*
        -	 * "ADMIN" is allowed to do anything
        -	 *
        -	 * default: admin/admin
        -	 */
        -	ADMIN( //
        -			Role.ADMIN,
        -			new byte[] { -73, 16, 18, -107, 69, 80, -112, 66, 61, 7, 22, -65, 33, -109, -119, 123, -55, 119, -7, 30, 37,
        -					51, 49, 83, 74, 28, -10, -18, -14, -72, -30, 10 }, //
        -			"admin".getBytes(StandardCharsets.ISO_8859_1) //
        -	);
        -
        -	/*
        -	 * all users; sorted in reverse order of importance
        -	 */
        -	private final static User[] USERS = new User[] { ADMIN, INSTALLER, OWNER, GUEST };
        -	private final static int KEY_LENGTH = 256;
        -	private final static int SALT_LENGTH = 32;
        -	private final static int ITERATIONS = 10;
        -	// was the user database initialized? Do not allow settings after initialization.
        -	private static boolean initialized = false;
        -	private final static Logger log = LoggerFactory.getLogger(User.class);
        -
        -	public static User[] getUsers() {
        -		return USERS;
        -	}
        -
        -	/**
        -	 * Get the User object for a given username.
        -	 *
        -	 * @param username
        -	 * @return User
        -	 * @throws OpenemsException
        -	 */
        -	public static User getUserByName(String username) throws OpenemsException {
        -		for (User user : USERS) {
        -			if (username.equals(user.getName())) {
        -				return user;
        -			}
        -		}
        -		throw new OpenemsException("Unable to find user [" + username + "].");
        -	}
        -
        -	private static byte[] getRandomSalt(int length) {
        -		SecureRandom sr = SecureRandomSingleton.getInstance();
        -		byte[] salt = new byte[length];
        -		sr.nextBytes(salt);
        -		return salt;
        -	}
        -
        -	/**
        -	 * Authenticates a user with his password
        -	 *
        -	 * @param password
        -	 * @return the authenticated User or null if authentication failed
        -	 */
        -	public static Optional authenticate(String password) {
        -		// Search for any user with the given password
        -		for (User user : USERS) {
        -			if (user.checkPassword(password)) {
        -				log.info("Authentication successful with password only for user [" + user.getName() + "].");
        -				return Optional.ofNullable(user);
        -			}
        -		}
        -		log.info("Authentication failed with password only.");
        -		return Optional.empty();
        -	}
        -
        -	public static Optional authenticate(String username, String password) {
        -		// Search for user with given username
        -		for (User user : USERS) {
        -			if (username.equals(user.getName())) {
        -				if (user.checkPassword(password)) {
        -					log.info("Authentication successful for user[" + username + "].");
        -					return Optional.of(user);
        -				} else {
        -					log.info("Authentication failed for user[" + username + "]: wrong password");
        -					return Optional.empty();
        -				}
        -			}
        -		}
        -		// Search for any user with the given password
        -		return authenticate(password);
        -	}
        -
        -	private static byte[] hashPassword(final String password, final byte[] salt) {
        -		return hashPassword(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
        -	}
        -
        -	public void initialize(String passwordBase64, String saltBase64) {
        -		if (User.initialized) {
        -			log.warn("User database has already been initialized!");
        -		} else {
        -			Decoder decoder = Base64.getDecoder();
        -			this.password = decoder.decode(passwordBase64);
        -			this.salt = decoder.decode(saltBase64);
        -		}
        -	}
        -
        -	public static void initializeFinished() {
        -		User.initialized = true;
        -	}
        -
        -	/**
        -	 * Source: https://www.owasp.org/index.php/Hashing_Java
        -	 *
        -	 * @param password
        -	 * @param salt
        -	 * @param iterations
        -	 * @param keyLength
        -	 * @return
        -	 */
        -	private static byte[] hashPassword(final char[] password, final byte[] salt, final int iterations,
        -			final int keyLength) {
        -		try {
        -			SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
        -			PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
        -			SecretKey key = skf.generateSecret(spec);
        -			byte[] res = key.getEncoded();
        -			return res;
        -
        -		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
        -			throw new RuntimeException(e);
        -		}
        -	}
        -
        -	/*
        -	 * This object
        -	 */
        -	private byte[] password;
        -	private byte[] salt;
        -	private final Role role;
        -
        -	private User(Role role, final byte[] password, final byte[] salt) {
        -		this.role = role;
        -		this.password = password;
        -		this.salt = salt;
        -	}
        -
        -	public void changePassword(String oldPassword, String newPassword) throws OpenemsException {
        -		if (checkPassword(oldPassword)) {
        -			byte[] salt = getRandomSalt(SALT_LENGTH);
        -			byte[] password = hashPassword(newPassword, salt);
        -			this.password = password;
        -			this.salt = salt;
        -			Config.getInstance().writeConfigFile();
        -		} else {
        -			throw new OpenemsException("Access denied. Old password was wrong.");
        -		}
        -	}
        -
        -	private boolean checkPassword(String password) {
        -		if (this.password == null || this.salt == null) {
        -			// no password existing -> allow access
        -			return true;
        -		}
        -		byte[] hashedPassword = hashPassword(password, this.salt);
        -		return Arrays.equals(hashedPassword, this.password);
        -	}
        -
        -	public byte[] getHashedPassword() {
        -		return password;
        -	}
        -
        -	public String getPasswordBase64() {
        -		return Base64.getEncoder().encodeToString(password);
        -	}
        -
        -	public byte[] getSalt() {
        -		return salt;
        -	}
        -
        -	public String getSaltBase64() {
        -		return Base64.getEncoder().encodeToString(salt);
        -	}
        -
        -	public String getName() {
        -		return name().toLowerCase();
        -	}
        -
        -	public Role getRole() {
        -		return this.role;
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.security;
        +
        +import java.nio.charset.StandardCharsets;
        +import java.security.NoSuchAlgorithmException;
        +import java.security.SecureRandom;
        +import java.security.spec.InvalidKeySpecException;
        +import java.util.Arrays;
        +import java.util.Base64;
        +import java.util.Base64.Decoder;
        +import java.util.Optional;
        +
        +import javax.crypto.SecretKey;
        +import javax.crypto.SecretKeyFactory;
        +import javax.crypto.spec.PBEKeySpec;
        +
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.session.Role;
        +import io.openems.common.utils.SecureRandomSingleton;
        +import io.openems.core.Config;
        +
        +public enum User {
        +	/*
        +	 * "GUEST" generally has readonly access
        +	 *
        +	 * default: guest/guest
        +	 */
        +	GUEST( //
        +			Role.GUEST,
        +			new byte[] { 33, -62, 51, 37, 35, -81, 52, -51, 79, -67, 15, 47, -25, 42, 69, -68, -6, 19, 103, 33, -16,
        +					-36, -87, -24, 111, -20, -30, -19, -33, -106, -78, -107 }, //
        +			"user".getBytes(StandardCharsets.ISO_8859_1) //
        +	), //
        +
        +	/*
        +	 * "OWNER" is the owner of the system.
        +	 *
        +	 * default: owner/owner
        +	 */
        +	OWNER( //
        +			Role.OWNER,
        +			new byte[] { 120, -104, 11, 5, -15, -45, -103, -24, 111, -31, 45, 112, -122, -57, -29, 120, 77, -22, -36, 2,
        +					102, 36, 32, 90, 109, 94, 125, 99, -82, 94, -95, -126 }, //
        +			"owner".getBytes(StandardCharsets.ISO_8859_1) //
        +	), //
        +	/*
        +	 * "INSTALLER" is a qualified electrician with extended configuration access
        +	 *
        +	 * default: installer/installer
        +	 */
        +	INSTALLER( //
        +			Role.INSTALLER,
        +			new byte[] { -40, -19, 93, 50, 91, 5, 119, 6, -97, -53, -97, 30, -122, -76, -2, 95, -19, 2, 17, 102, -128,
        +					-104, 20, 90, 119, -110, 69, 109, 50, -15, -3, 106 }, //
        +			"installer".getBytes(StandardCharsets.ISO_8859_1) //
        +	), //
        +	/*
        +	 * "ADMIN" is allowed to do anything
        +	 *
        +	 * default: admin/admin
        +	 */
        +	ADMIN( //
        +			Role.ADMIN,
        +			new byte[] { -73, 16, 18, -107, 69, 80, -112, 66, 61, 7, 22, -65, 33, -109, -119, 123, -55, 119, -7, 30, 37,
        +					51, 49, 83, 74, 28, -10, -18, -14, -72, -30, 10 }, //
        +			"admin".getBytes(StandardCharsets.ISO_8859_1) //
        +	);
        +
        +	/*
        +	 * all users; sorted in reverse order of importance
        +	 */
        +	private final static User[] USERS = new User[] { ADMIN, INSTALLER, OWNER, GUEST };
        +	private final static int KEY_LENGTH = 256;
        +	private final static int SALT_LENGTH = 32;
        +	private final static int ITERATIONS = 10;
        +	// was the user database initialized? Do not allow settings after initialization.
        +	private static boolean initialized = false;
        +	private final static Logger log = LoggerFactory.getLogger(User.class);
        +
        +	public static User[] getUsers() {
        +		return USERS;
        +	}
        +
        +	/**
        +	 * Get the User object for a given username.
        +	 *
        +	 * @param username
        +	 * @return User
        +	 * @throws OpenemsException
        +	 */
        +	public static User getUserByName(String username) throws OpenemsException {
        +		for (User user : USERS) {
        +			if (username.equals(user.getName())) {
        +				return user;
        +			}
        +		}
        +		throw new OpenemsException("Unable to find user [" + username + "].");
        +	}
        +
        +	private static byte[] getRandomSalt(int length) {
        +		SecureRandom sr = SecureRandomSingleton.getInstance();
        +		byte[] salt = new byte[length];
        +		sr.nextBytes(salt);
        +		return salt;
        +	}
        +
        +	/**
        +	 * Authenticates a user with his password
        +	 *
        +	 * @param password
        +	 * @return the authenticated User or null if authentication failed
        +	 */
        +	public static Optional authenticate(String password) {
        +		// Search for any user with the given password
        +		for (User user : USERS) {
        +			if (user.checkPassword(password)) {
        +				log.info("Authentication successful with password only for user [" + user.getName() + "].");
        +				return Optional.ofNullable(user);
        +			}
        +		}
        +		log.info("Authentication failed with password only.");
        +		return Optional.empty();
        +	}
        +
        +	public static Optional authenticate(String username, String password) {
        +		// Search for user with given username
        +		for (User user : USERS) {
        +			if (username.equals(user.getName())) {
        +				if (user.checkPassword(password)) {
        +					log.info("Authentication successful for user[" + username + "].");
        +					return Optional.of(user);
        +				} else {
        +					log.info("Authentication failed for user[" + username + "]: wrong password");
        +					return Optional.empty();
        +				}
        +			}
        +		}
        +		// Search for any user with the given password
        +		return authenticate(password);
        +	}
        +
        +	private static byte[] hashPassword(final String password, final byte[] salt) {
        +		return hashPassword(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
        +	}
        +
        +	public void initialize(String passwordBase64, String saltBase64) {
        +		if (User.initialized) {
        +			log.warn("User database has already been initialized!");
        +		} else {
        +			Decoder decoder = Base64.getDecoder();
        +			this.password = decoder.decode(passwordBase64);
        +			this.salt = decoder.decode(saltBase64);
        +		}
        +	}
        +
        +	public static void initializeFinished() {
        +		User.initialized = true;
        +	}
        +
        +	/**
        +	 * Source: https://www.owasp.org/index.php/Hashing_Java
        +	 *
        +	 * @param password
        +	 * @param salt
        +	 * @param iterations
        +	 * @param keyLength
        +	 * @return
        +	 */
        +	private static byte[] hashPassword(final char[] password, final byte[] salt, final int iterations,
        +			final int keyLength) {
        +		try {
        +			SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
        +			PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
        +			SecretKey key = skf.generateSecret(spec);
        +			byte[] res = key.getEncoded();
        +			return res;
        +
        +		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
        +			throw new RuntimeException(e);
        +		}
        +	}
        +
        +	/*
        +	 * This object
        +	 */
        +	private byte[] password;
        +	private byte[] salt;
        +	private final Role role;
        +
        +	private User(Role role, final byte[] password, final byte[] salt) {
        +		this.role = role;
        +		this.password = password;
        +		this.salt = salt;
        +	}
        +
        +	public void changePassword(String oldPassword, String newPassword) throws OpenemsException {
        +		if (checkPassword(oldPassword)) {
        +			byte[] salt = getRandomSalt(SALT_LENGTH);
        +			byte[] password = hashPassword(newPassword, salt);
        +			this.password = password;
        +			this.salt = salt;
        +			Config.getInstance().writeConfigFile();
        +		} else {
        +			throw new OpenemsException("Access denied. Old password was wrong.");
        +		}
        +	}
        +
        +	private boolean checkPassword(String password) {
        +		if (this.password == null || this.salt == null) {
        +			// no password existing -> allow access
        +			return true;
        +		}
        +		byte[] hashedPassword = hashPassword(password, this.salt);
        +		return Arrays.equals(hashedPassword, this.password);
        +	}
        +
        +	public byte[] getHashedPassword() {
        +		return password;
        +	}
        +
        +	public String getPasswordBase64() {
        +		return Base64.getEncoder().encodeToString(password);
        +	}
        +
        +	public byte[] getSalt() {
        +		return salt;
        +	}
        +
        +	public String getSaltBase64() {
        +		return Base64.getEncoder().encodeToString(salt);
        +	}
        +
        +	public String getName() {
        +		return name().toLowerCase();
        +	}
        +
        +	public Role getRole() {
        +		return this.role;
        +	}
        +}
        diff --git a/edge/src/io/openems/core/ClassRepository.java b/edge/src/io/openems/core/ClassRepository.java
        index 12dbc346edd..331b7d8ada0 100644
        --- a/edge/src/io/openems/core/ClassRepository.java
        +++ b/edge/src/io/openems/core/ClassRepository.java
        @@ -1,285 +1,285 @@
        -/*******************************************************************************
        - * 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.core;
        -
        -import java.lang.reflect.Field;
        -import java.lang.reflect.Method;
        -import java.util.ArrayList;
        -import java.util.Collection;
        -import java.util.Collections;
        -import java.util.HashMap;
        -import java.util.HashSet;
        -import java.util.Optional;
        -import java.util.Set;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import com.google.common.collect.Iterables;
        -
        -import io.openems.api.bridge.Bridge;
        -import io.openems.api.channel.Channel;
        -import io.openems.api.channel.ConfigChannel;
        -import io.openems.api.controller.Controller;
        -import io.openems.api.device.Device;
        -import io.openems.api.device.nature.DeviceNature;
        -import io.openems.api.doc.ChannelDoc;
        -import io.openems.api.doc.ChannelInfo;
        -import io.openems.api.doc.ThingDoc;
        -import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.ReflectionException;
        -import io.openems.api.persistence.Persistence;
        -import io.openems.api.scheduler.Scheduler;
        -import io.openems.api.thing.Thing;
        -import io.openems.core.utilities.ConfigUtils;
        -
        -/**
        - * Retreives and caches information about classes via reflection
        - *
        - * @author stefan.feilmeier
        - */
        -public class ClassRepository {
        -	private final static Logger log = LoggerFactory.getLogger(ClassRepository.class);
        -	private static ClassRepository instance;
        -
        -	public static synchronized ClassRepository getInstance() {
        -		if (ClassRepository.instance == null) {
        -			ClassRepository.instance = new ClassRepository();
        -		}
        -		return ClassRepository.instance;
        -	}
        -
        -	private Set> bridges = new HashSet<>();
        -	private Set> schedulers = new HashSet<>();
        -	private Set> devices = new HashSet<>();
        -	private Set> deviceNatures = new HashSet<>();
        -	private Set> controllers = new HashSet<>();
        -	private Set> persistences = new HashSet<>();
        -	private HashMap, ThingDoc> thingDocs = new HashMap<>();
        -
        -	public ClassRepository() {}
        -
        -	public Iterable getAvailableThings() throws ReflectionException {
        -		return Iterables.concat( //
        -				getAvailableBridges(), //
        -				getAvailableControllers(), //
        -				getAvailableDevices(), //
        -				getAvailableDeviceNatures(), //
        -				getAvailableSchedulers(), //
        -				getAvailablePersistences());
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public Collection getAvailableControllers() throws ReflectionException {
        -		// update cache of available controllers
        -		if (this.controllers.isEmpty()) {
        -			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.controller",
        -					Controller.class, "Controller")) {
        -				this.controllers.add((Class) clazz);
        -			}
        -		}
        -		// create result
        -		Collection controllerDocs = new ArrayList<>();
        -		for (Class clazz : this.controllers) {
        -			controllerDocs.add(this.getThingDoc(clazz));
        -		}
        -		return Collections.unmodifiableCollection(controllerDocs);
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public Collection getAvailableBridges() throws ReflectionException {
        -		// update cache of available bridges
        -		if (this.bridges.isEmpty()) {
        -			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.protocol",
        -					Bridge.class, "")) {
        -				this.bridges.add((Class) clazz);
        -			}
        -		}
        -		// create result
        -		Collection bridgeDocs = new ArrayList<>();
        -		for (Class clazz : this.bridges) {
        -			bridgeDocs.add(this.getThingDoc(clazz));
        -		}
        -		return Collections.unmodifiableCollection(bridgeDocs);
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public Collection getAvailableDevices() throws ReflectionException {
        -		// update cache of available devices
        -		if (devices.isEmpty()) {
        -			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.device", Device.class,
        -					"")) {
        -				this.devices.add((Class) clazz);
        -			}
        -		}
        -		// create result
        -		Collection deviceDocs = new ArrayList<>();
        -		for (Class clazz : this.devices) {
        -			deviceDocs.add(this.getThingDoc(clazz));
        -		}
        -		return Collections.unmodifiableCollection(deviceDocs);
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public Collection getAvailableDeviceNatures() throws ReflectionException {
        -		// TODO merge with getAvailableNatures to avoid parsing twice
        -		// update cache of available device natures
        -		if (deviceNatures.isEmpty()) {
        -			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.device",
        -					DeviceNature.class, "")) {
        -				this.deviceNatures.add((Class) clazz);
        -			}
        -		}
        -		// create result
        -		Collection deviceNatureDocs = new ArrayList<>();
        -		for (Class clazz : this.deviceNatures) {
        -			deviceNatureDocs.add(this.getThingDoc(clazz));
        -		}
        -		return Collections.unmodifiableCollection(deviceNatureDocs);
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public Collection getAvailableSchedulers() throws ReflectionException {
        -		// update cache of available schedulers
        -		if (this.schedulers.isEmpty()) {
        -			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.scheduler",
        -					Scheduler.class, "Scheduler")) {
        -				this.schedulers.add((Class) clazz);
        -			}
        -		}
        -		// create result
        -		Collection schedulerDocs = new ArrayList<>();
        -		for (Class clazz : this.schedulers) {
        -			schedulerDocs.add(this.getThingDoc(clazz));
        -		}
        -		return Collections.unmodifiableCollection(schedulerDocs);
        -	}
        -
        -	@SuppressWarnings("unchecked")
        -	public Collection getAvailablePersistences() throws ReflectionException {
        -		// update cache of available schedulers
        -		if (this.persistences.isEmpty()) {
        -			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.persistence",
        -					Persistence.class, "Persistence")) {
        -				this.persistences.add((Class) clazz);
        -			}
        -		}
        -		// create result
        -		Collection persistenceDocs = new ArrayList<>();
        -		for (Class clazz : this.persistences) {
        -			persistenceDocs.add(this.getThingDoc(clazz));
        -		}
        -		return Collections.unmodifiableCollection(persistenceDocs);
        -	}
        -
        -	/**
        -	 * Returns the cached ThingDoc or parses the class and adds it to the cache.
        -	 * Field annotations have higher priority than method annotations!
        -	 *
        -	 * @param clazz
        -	 */
        -	public ThingDoc getThingDoc(Class clazz) {
        -		if (this.thingDocs.containsKey(clazz)) {
        -			// return from cache
        -			return this.thingDocs.get(clazz);
        -		}
        -		ThingDoc thingDoc = new ThingDoc(clazz);
        -
        -		// get info about thing
        -		ThingInfo thing = clazz.getAnnotation(ThingInfo.class);
        -		if (thing == null) {
        -			log.warn("Thing [" + clazz.getName() + "] has no @ThingInfo annotation");
        -		} else {
        -			thingDoc.setThingDescription(thing);
        -		}
        -
        -		// parse all methods
        -		for (Method method : clazz.getMethods()) {
        -			Class type = null;
        -			if (method.getReturnType().isArray()) {
        -				Class rtype = method.getReturnType();
        -				type = rtype.getComponentType();
        -			} else {
        -				type = method.getReturnType();
        -			}
        -			if (Channel.class.isAssignableFrom(type)) {
        -				Optional channelInfoOpt = getAnnotationForMethod(clazz, method.getName());
        -				String channelId = method.getName();
        -				ChannelDoc channelDoc = new ChannelDoc(method, channelId, channelInfoOpt);
        -				thingDoc.addChannelDoc(channelDoc);
        -				if (ConfigChannel.class.isAssignableFrom(type)) {
        -					thingDoc.addConfigChannelDoc(channelDoc);
        -				}
        -			}
        -		}
        -		// parse all fields
        -		for (Field field : clazz.getFields()) {
        -			Class type = field.getType();
        -			if (Channel.class.isAssignableFrom(type)) {
        -				String channelId = field.getName();
        -				ChannelDoc channelDoc = new ChannelDoc(field, channelId,
        -						Optional.ofNullable(field.getAnnotation(ChannelInfo.class)));
        -				thingDoc.addChannelDoc(channelDoc);
        -				if (ConfigChannel.class.isAssignableFrom(type)) {
        -					thingDoc.addConfigChannelDoc(channelDoc);
        -				}
        -			}
        -		}
        -		// add to cache
        -		this.thingDocs.put(clazz, thingDoc);
        -
        -		return thingDoc;
        -	}
        -
        -	/**
        -	 * Tries to find the annotation of the method in the class hierarchy
        -	 *
        -	 * @param clazz
        -	 * @return
        -	 */
        -	private Optional getAnnotationForMethod(Class clazz, String methodName) {
        -		Method method;
        -		try {
        -			method = clazz.getMethod(methodName);
        -		} catch (NoSuchMethodException | SecurityException e) {
        -			return Optional.empty();
        -		}
        -		if (method.isAnnotationPresent(ChannelInfo.class)) {
        -			// found annotation
        -			return Optional.of(method.getAnnotation(ChannelInfo.class));
        -		} else {
        -			Class superclazz = clazz.getSuperclass();
        -			if (superclazz != null) {
        -				Optional channelInfo = getAnnotationForMethod(superclazz, methodName);
        -				if (channelInfo.isPresent()) {
        -					return channelInfo;
        -				}
        -			}
        -			for (Class iface : clazz.getInterfaces()) {
        -				Optional channelInfo = getAnnotationForMethod(iface, methodName);
        -				if (channelInfo.isPresent()) {
        -					return channelInfo;
        -				}
        -			}
        -		}
        -		return Optional.empty();
        -	}
        -}
        +/*******************************************************************************
        + * 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.core;
        +
        +import java.lang.reflect.Field;
        +import java.lang.reflect.Method;
        +import java.util.ArrayList;
        +import java.util.Collection;
        +import java.util.Collections;
        +import java.util.HashMap;
        +import java.util.HashSet;
        +import java.util.Optional;
        +import java.util.Set;
        +
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import com.google.common.collect.Iterables;
        +
        +import io.openems.api.bridge.Bridge;
        +import io.openems.api.channel.Channel;
        +import io.openems.api.channel.ConfigChannel;
        +import io.openems.api.controller.Controller;
        +import io.openems.api.device.Device;
        +import io.openems.api.device.nature.DeviceNature;
        +import io.openems.api.doc.ChannelDoc;
        +import io.openems.api.doc.ChannelInfo;
        +import io.openems.api.doc.ThingDoc;
        +import io.openems.api.doc.ThingInfo;
        +import io.openems.api.exception.ReflectionException;
        +import io.openems.api.persistence.Persistence;
        +import io.openems.api.scheduler.Scheduler;
        +import io.openems.api.thing.Thing;
        +import io.openems.core.utilities.ConfigUtils;
        +
        +/**
        + * Retreives and caches information about classes via reflection
        + *
        + * @author stefan.feilmeier
        + */
        +public class ClassRepository {
        +	private final static Logger log = LoggerFactory.getLogger(ClassRepository.class);
        +	private static ClassRepository instance;
        +
        +	public static synchronized ClassRepository getInstance() {
        +		if (ClassRepository.instance == null) {
        +			ClassRepository.instance = new ClassRepository();
        +		}
        +		return ClassRepository.instance;
        +	}
        +
        +	private Set> bridges = new HashSet<>();
        +	private Set> schedulers = new HashSet<>();
        +	private Set> devices = new HashSet<>();
        +	private Set> deviceNatures = new HashSet<>();
        +	private Set> controllers = new HashSet<>();
        +	private Set> persistences = new HashSet<>();
        +	private HashMap, ThingDoc> thingDocs = new HashMap<>();
        +
        +	public ClassRepository() {}
        +
        +	public Iterable getAvailableThings() throws ReflectionException {
        +		return Iterables.concat( //
        +				getAvailableBridges(), //
        +				getAvailableControllers(), //
        +				getAvailableDevices(), //
        +				getAvailableDeviceNatures(), //
        +				getAvailableSchedulers(), //
        +				getAvailablePersistences());
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public Collection getAvailableControllers() throws ReflectionException {
        +		// update cache of available controllers
        +		if (this.controllers.isEmpty()) {
        +			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.controller",
        +					Controller.class, "Controller")) {
        +				this.controllers.add((Class) clazz);
        +			}
        +		}
        +		// create result
        +		Collection controllerDocs = new ArrayList<>();
        +		for (Class clazz : this.controllers) {
        +			controllerDocs.add(this.getThingDoc(clazz));
        +		}
        +		return Collections.unmodifiableCollection(controllerDocs);
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public Collection getAvailableBridges() throws ReflectionException {
        +		// update cache of available bridges
        +		if (this.bridges.isEmpty()) {
        +			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.protocol",
        +					Bridge.class, "")) {
        +				this.bridges.add((Class) clazz);
        +			}
        +		}
        +		// create result
        +		Collection bridgeDocs = new ArrayList<>();
        +		for (Class clazz : this.bridges) {
        +			bridgeDocs.add(this.getThingDoc(clazz));
        +		}
        +		return Collections.unmodifiableCollection(bridgeDocs);
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public Collection getAvailableDevices() throws ReflectionException {
        +		// update cache of available devices
        +		if (devices.isEmpty()) {
        +			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.device", Device.class,
        +					"")) {
        +				this.devices.add((Class) clazz);
        +			}
        +		}
        +		// create result
        +		Collection deviceDocs = new ArrayList<>();
        +		for (Class clazz : this.devices) {
        +			deviceDocs.add(this.getThingDoc(clazz));
        +		}
        +		return Collections.unmodifiableCollection(deviceDocs);
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public Collection getAvailableDeviceNatures() throws ReflectionException {
        +		// TODO merge with getAvailableNatures to avoid parsing twice
        +		// update cache of available device natures
        +		if (deviceNatures.isEmpty()) {
        +			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.device",
        +					DeviceNature.class, "")) {
        +				this.deviceNatures.add((Class) clazz);
        +			}
        +		}
        +		// create result
        +		Collection deviceNatureDocs = new ArrayList<>();
        +		for (Class clazz : this.deviceNatures) {
        +			deviceNatureDocs.add(this.getThingDoc(clazz));
        +		}
        +		return Collections.unmodifiableCollection(deviceNatureDocs);
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public Collection getAvailableSchedulers() throws ReflectionException {
        +		// update cache of available schedulers
        +		if (this.schedulers.isEmpty()) {
        +			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.scheduler",
        +					Scheduler.class, "Scheduler")) {
        +				this.schedulers.add((Class) clazz);
        +			}
        +		}
        +		// create result
        +		Collection schedulerDocs = new ArrayList<>();
        +		for (Class clazz : this.schedulers) {
        +			schedulerDocs.add(this.getThingDoc(clazz));
        +		}
        +		return Collections.unmodifiableCollection(schedulerDocs);
        +	}
        +
        +	@SuppressWarnings("unchecked")
        +	public Collection getAvailablePersistences() throws ReflectionException {
        +		// update cache of available schedulers
        +		if (this.persistences.isEmpty()) {
        +			for (Class clazz : ConfigUtils.getAvailableClasses("io.openems.impl.persistence",
        +					Persistence.class, "Persistence")) {
        +				this.persistences.add((Class) clazz);
        +			}
        +		}
        +		// create result
        +		Collection persistenceDocs = new ArrayList<>();
        +		for (Class clazz : this.persistences) {
        +			persistenceDocs.add(this.getThingDoc(clazz));
        +		}
        +		return Collections.unmodifiableCollection(persistenceDocs);
        +	}
        +
        +	/**
        +	 * Returns the cached ThingDoc or parses the class and adds it to the cache.
        +	 * Field annotations have higher priority than method annotations!
        +	 *
        +	 * @param clazz
        +	 */
        +	public ThingDoc getThingDoc(Class clazz) {
        +		if (this.thingDocs.containsKey(clazz)) {
        +			// return from cache
        +			return this.thingDocs.get(clazz);
        +		}
        +		ThingDoc thingDoc = new ThingDoc(clazz);
        +
        +		// get info about thing
        +		ThingInfo thing = clazz.getAnnotation(ThingInfo.class);
        +		if (thing == null) {
        +			log.warn("Thing [" + clazz.getName() + "] has no @ThingInfo annotation");
        +		} else {
        +			thingDoc.setThingDescription(thing);
        +		}
        +
        +		// parse all methods
        +		for (Method method : clazz.getMethods()) {
        +			Class type = null;
        +			if (method.getReturnType().isArray()) {
        +				Class rtype = method.getReturnType();
        +				type = rtype.getComponentType();
        +			} else {
        +				type = method.getReturnType();
        +			}
        +			if (Channel.class.isAssignableFrom(type)) {
        +				Optional channelInfoOpt = getAnnotationForMethod(clazz, method.getName());
        +				String channelId = method.getName();
        +				ChannelDoc channelDoc = new ChannelDoc(method, channelId, channelInfoOpt);
        +				thingDoc.addChannelDoc(channelDoc);
        +				if (ConfigChannel.class.isAssignableFrom(type)) {
        +					thingDoc.addConfigChannelDoc(channelDoc);
        +				}
        +			}
        +		}
        +		// parse all fields
        +		for (Field field : clazz.getFields()) {
        +			Class type = field.getType();
        +			if (Channel.class.isAssignableFrom(type)) {
        +				String channelId = field.getName();
        +				ChannelDoc channelDoc = new ChannelDoc(field, channelId,
        +						Optional.ofNullable(field.getAnnotation(ChannelInfo.class)));
        +				thingDoc.addChannelDoc(channelDoc);
        +				if (ConfigChannel.class.isAssignableFrom(type)) {
        +					thingDoc.addConfigChannelDoc(channelDoc);
        +				}
        +			}
        +		}
        +		// add to cache
        +		this.thingDocs.put(clazz, thingDoc);
        +
        +		return thingDoc;
        +	}
        +
        +	/**
        +	 * Tries to find the annotation of the method in the class hierarchy
        +	 *
        +	 * @param clazz
        +	 * @return
        +	 */
        +	private Optional getAnnotationForMethod(Class clazz, String methodName) {
        +		Method method;
        +		try {
        +			method = clazz.getMethod(methodName);
        +		} catch (NoSuchMethodException | SecurityException e) {
        +			return Optional.empty();
        +		}
        +		if (method.isAnnotationPresent(ChannelInfo.class)) {
        +			// found annotation
        +			return Optional.of(method.getAnnotation(ChannelInfo.class));
        +		} else {
        +			Class superclazz = clazz.getSuperclass();
        +			if (superclazz != null) {
        +				Optional channelInfo = getAnnotationForMethod(superclazz, methodName);
        +				if (channelInfo.isPresent()) {
        +					return channelInfo;
        +				}
        +			}
        +			for (Class iface : clazz.getInterfaces()) {
        +				Optional channelInfo = getAnnotationForMethod(iface, methodName);
        +				if (channelInfo.isPresent()) {
        +					return channelInfo;
        +				}
        +			}
        +		}
        +		return Optional.empty();
        +	}
        +}
        diff --git a/edge/src/io/openems/core/Config.java b/edge/src/io/openems/core/Config.java
        index 981d0de72c1..560831d5136 100644
        --- a/edge/src/io/openems/core/Config.java
        +++ b/edge/src/io/openems/core/Config.java
        @@ -53,18 +53,17 @@
         import io.openems.api.device.Device;
         import io.openems.api.doc.ThingDoc;
         import io.openems.api.exception.ConfigException;
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.NotImplementedException;
         import io.openems.api.exception.ReflectionException;
        -import io.openems.api.exception.WriteChannelException;
         import io.openems.api.persistence.Persistence;
         import io.openems.api.scheduler.Scheduler;
         import io.openems.api.security.User;
         import io.openems.api.thing.Thing;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.session.Role;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.utilities.ConfigUtils;
         import io.openems.core.utilities.InjectionUtils;
        -import io.openems.core.utilities.JsonUtils;
         
         public class Config implements ChannelChangeListener {
         
        @@ -241,7 +240,7 @@ private JsonObject addDefaultConfig(JsonObject jConfig) {
         				jScheduler.add("controllers", jControllers);
         			}
         			jConfig.add("scheduler", jScheduler);
        -		} catch (ReflectionException e) {
        +		} catch (OpenemsException e) {
         			log.warn("Error applying default config: " + e.getMessage());
         		}
         		return jConfig;
        @@ -300,7 +299,7 @@ public void run() {
         	}
         
         	public synchronized void parseJsonConfig(JsonObject jConfig)
        -			throws ReflectionException, ConfigException, WriteChannelException {
        +			throws OpenemsException {
         		/*
         		 * read Users
         		 */
        diff --git a/edge/src/io/openems/core/ThingRepository.java b/edge/src/io/openems/core/ThingRepository.java
        index 08f427dc403..07cc649b69a 100644
        --- a/edge/src/io/openems/core/ThingRepository.java
        +++ b/edge/src/io/openems/core/ThingRepository.java
        @@ -1,567 +1,567 @@
        -/*******************************************************************************
        - * 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.core;
        -
        -import java.lang.reflect.Field;
        -import java.lang.reflect.InvocationTargetException;
        -import java.lang.reflect.Member;
        -import java.lang.reflect.Method;
        -import java.util.ArrayList;
        -import java.util.Collection;
        -import java.util.Collections;
        -import java.util.HashSet;
        -import java.util.Iterator;
        -import java.util.LinkedList;
        -import java.util.List;
        -import java.util.Map;
        -import java.util.Map.Entry;
        -import java.util.Optional;
        -import java.util.Set;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import com.google.common.collect.BiMap;
        -import com.google.common.collect.HashBasedTable;
        -import com.google.common.collect.HashBiMap;
        -import com.google.common.collect.HashMultimap;
        -import com.google.common.collect.Table;
        -import com.google.gson.JsonObject;
        -
        -import io.openems.api.bridge.Bridge;
        -import io.openems.api.channel.Channel;
        -import io.openems.api.channel.ConfigChannel;
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.Controller;
        -import io.openems.api.device.Device;
        -import io.openems.api.device.nature.DeviceNature;
        -import io.openems.api.doc.ChannelDoc;
        -import io.openems.api.doc.ThingDoc;
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.api.exception.ReflectionException;
        -import io.openems.api.persistence.Persistence;
        -import io.openems.api.persistence.QueryablePersistence;
        -import io.openems.api.scheduler.Scheduler;
        -import io.openems.api.thing.Thing;
        -import io.openems.api.thing.ThingChannelsUpdatedListener;
        -import io.openems.common.types.ChannelAddress;
        -import io.openems.core.ThingsChangedListener.Action;
        -import io.openems.core.utilities.ConfigUtils;
        -import io.openems.core.utilities.InjectionUtils;
        -import io.openems.core.utilities.JsonUtils;
        -
        -public class ThingRepository implements ThingChannelsUpdatedListener {
        -	private final static Logger log = LoggerFactory.getLogger(ThingRepository.class);
        -
        -	private static ThingRepository instance;
        -
        -	public static synchronized ThingRepository getInstance() {
        -		if (ThingRepository.instance == null) {
        -			ThingRepository.instance = new ThingRepository();
        -		}
        -		return ThingRepository.instance;
        -	}
        -
        -	private ThingRepository() {
        -		classRepository = ClassRepository.getInstance();
        -	}
        -
        -	private final ClassRepository classRepository;
        -	private final BiMap thingIds = HashBiMap.create();
        -	private HashMultimap, Thing> thingClasses = HashMultimap.create();
        -	private Set bridges = new HashSet<>();
        -	// TODO scheduler should not be a set, but only one value
        -	private Set schedulers = new HashSet<>();
        -	private Set persistences = new HashSet<>();
        -	private Set queryablePersistences = new HashSet<>();
        -	private Set deviceNatures = new HashSet<>();
        -	private final Table thingChannels = HashBasedTable.create();
        -	private HashMultimap> thingConfigChannels = HashMultimap.create();
        -	private HashMultimap> thingWriteChannels = HashMultimap.create();
        -	private List thingListeners = new LinkedList<>();
        -
        -	public void addThingChangedListener(ThingsChangedListener listener) {
        -		this.thingListeners.add(listener);
        -	}
        -
        -	public void removeThingChangedListener(ThingsChangedListener listener) {
        -		this.thingListeners.remove(listener);
        -	}
        -
        -	/**
        -	 * Add a Thing to the Repository and cache its Channels and other information for later usage.
        -	 *
        -	 * @param thing
        -	 */
        -	public synchronized void addThing(Thing thing) {
        -		if (thingIds.containsValue(thing)) {
        -			// Thing was already added
        -			return;
        -		}
        -		// Add to thingIds
        -		thingIds.forcePut(thing.id(), thing);
        -
        -		// Add to thingClasses
        -		thingClasses.put(thing.getClass(), thing);
        -
        -		// Add to bridges
        -		if (thing instanceof Bridge) {
        -			bridges.add((Bridge) thing);
        -		}
        -
        -		// Add to schedulers
        -		if (thing instanceof Scheduler) {
        -			schedulers.add((Scheduler) thing);
        -		}
        -
        -		// Add to persistences
        -		if (thing instanceof Persistence) {
        -			persistences.add((Persistence) thing);
        -		}
        -
        -		// Add to queryablePersistences
        -		if (thing instanceof QueryablePersistence) {
        -			queryablePersistences.add((QueryablePersistence) thing);
        -		}
        -
        -		// Add to device natures
        -		if (thing instanceof DeviceNature) {
        -			deviceNatures.add((DeviceNature) thing);
        -		}
        -
        -		// Add Listener
        -		thing.addListener(this);
        -
        -		// Apply channel annotation (this happens now and again after initializing the thing via init()
        -		this.applyChannelAnnotation(thing);
        -
        -		// Add Channels thingConfigChannels
        -		ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass());
        -		for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) {
        -			Member member = channelDoc.getMember();
        -			try {
        -				List channels = new ArrayList<>();
        -				java.util.function.Consumer addToChannels = (c) -> {
        -					if(c == null) {
        -						log.error(
        -								"Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]");
        -					} else {
        -						channels.add(c);
        -					}
        -				};
        -				if (member instanceof Method) {
        -					if (((Method) member).getReturnType().isArray()) {
        -						Channel[] ch = (Channel[]) ((Method) member).invoke(thing);
        -						for (Channel c : ch) {
        -							addToChannels.accept(c);
        -						}
        -					} else {
        -						// It's a Method with ReturnType Channel
        -						Channel c = (Channel) ((Method) member).invoke(thing);
        -						addToChannels.accept(c);
        -					}
        -				} else if (member instanceof Field) {
        -					// It's a Field with Type Channel
        -					Channel c = (Channel) ((Field) member).get(thing);
        -					addToChannels.accept(c);
        -				} else {
        -					continue;
        -				}
        -				if (channels.isEmpty()) {
        -					continue;
        -				}
        -				for (Channel channel : channels) {
        -					// Add Channel to thingChannels
        -					thingChannels.put(thing, channel.id(), channel);
        -					if (channel instanceof ConfigChannel) {
        -
        -						// Add Channel to configChannels
        -						thingConfigChannels.put(thing, (ConfigChannel) channel);
        -					}
        -				}
        -			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        -				log.warn("Unable to add Channel. Member [" + member.getName() + "]", e);
        -			}
        -		}
        -		for (ThingsChangedListener listener : thingListeners) {
        -			listener.thingChanged(thing, Action.ADD);
        -		}
        -	}
        -
        -	public void applyChannelAnnotation(Thing thing) {
        -		ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass());
        -		for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) {
        -			try {
        -				Channel channel = getChannel(thing, channelDoc.getMember());
        -				channel.setChannelDoc(channelDoc);
        -			} catch (OpenemsException e) {
        -				log.debug(e.getMessage());
        -			}
        -		}
        -	}
        -
        -	/**
        -	 * Remove a Thing from the Repository.
        -	 *
        -	 * @param thing
        -	 */
        -	public synchronized void removeThing(String thingId) {
        -		Thing thing = thingIds.get(thingId);
        -		removeThing(thing);
        -	}
        -
        -	/**
        -	 * Remove a Thing from the Repository.
        -	 *
        -	 * @param thing
        -	 */
        -	public synchronized void removeThing(Thing thing) {
        -		// Remove from thingIds
        -		thingIds.remove(thing.id());
        -
        -		// Remove from thingClasses
        -		thingClasses.remove(thing.getClass(), thing);
        -
        -		// Remove from bridges
        -		if (thing instanceof Bridge) {
        -			bridges.remove(thing);
        -		}
        -
        -		// Remove from schedulers
        -		if (thing instanceof Scheduler) {
        -			schedulers.remove(thing);
        -		}
        -
        -		// Remove from persistences
        -		if (thing instanceof Persistence) {
        -			persistences.remove(thing);
        -		}
        -
        -		// Remove from queryablePersistences
        -		if (thing instanceof QueryablePersistence) {
        -			queryablePersistences.remove(thing);
        -		}
        -
        -		// Remove from deviceNatures
        -		if (thing instanceof DeviceNature) {
        -			deviceNatures.remove(thing);
        -		}
        -
        -		// Remove controller
        -		if (thing instanceof Controller) {
        -			Controller controller = (Controller) thing;
        -			for (Scheduler scheduler : getSchedulers()) {
        -				scheduler.removeController(controller);
        -			}
        -		}
        -
        -		// Remove device
        -		if (thing instanceof Device) {
        -			for (Bridge bridge : bridges) {
        -				bridge.removeDevice((Device) thing);
        -			}
        -		}
        -
        -		// Remove Listener
        -		thing.removeListener(this);
        -		// TODO further cleaning if required
        -		for (ThingsChangedListener listener : thingListeners) {
        -			listener.thingChanged(thing, Action.REMOVE);
        -		}
        -	}
        -
        -	public Thing getThing(String thingId) {
        -		Thing thing = thingIds.get(thingId);
        -		return thing;
        -	}
        -
        -	public Set getThings() {
        -		return Collections.unmodifiableSet(this.thingIds.values());
        -	}
        -
        -	/**
        -	 * Returns all Channels for this Thing.
        -	 *
        -	 * @param thing
        -	 * @return
        -	 */
        -	public synchronized Collection getChannels(Thing thing) {
        -		return Collections.unmodifiableCollection(thingChannels.row(thing).values());
        -	}
        -
        -	/**
        -	 * Returns all Config-Channels.
        -	 *
        -	 * @return
        -	 */
        -	public synchronized Collection> getConfigChannels() {
        -		return Collections.unmodifiableCollection(thingConfigChannels.values());
        -	}
        -
        -	/**
        -	 * Returns all Config-Channels for this Thing.
        -	 *
        -	 * @param thing
        -	 * @return
        -	 */
        -	public synchronized Set> getConfigChannels(Thing thing) {
        -		return Collections.unmodifiableSet(thingConfigChannels.get(thing));
        -	}
        -
        -	/**
        -	 * Returns all Write-Channels for this Thing.
        -	 *
        -	 * @param thing
        -	 * @return
        -	 */
        -	public synchronized Set> getWriteChannels(Thing thing) {
        -		return Collections.unmodifiableSet(thingWriteChannels.get(thing));
        -	}
        -
        -	/**
        -	 * Returns all Write-Channels.
        -	 *
        -	 * @param thing
        -	 * @return
        -	 */
        -	public synchronized Collection> getWriteChannels() {
        -		return Collections.unmodifiableCollection(thingWriteChannels.values());
        -	}
        -
        -	/**
        -	 * Returns all Persistence-Workers.
        -	 *
        -	 * @param thing
        -	 * @return
        -	 */
        -	public synchronized Set getPersistences() {
        -		return Collections.unmodifiableSet(persistences);
        -	}
        -
        -	/**
        -	 * Returns the ChannelDoc for a given Channel
        -	 *
        -	 * @param channelAddress
        -	 * @return
        -	 */
        -	public synchronized Optional getChannelDoc(ChannelAddress channelAddress) {
        -		Thing thing = getThing(channelAddress.getThingId());
        -		if (thing == null) {
        -			return Optional.empty();
        -		}
        -		ThingDoc thingDoc = ClassRepository.getInstance().getThingDoc(thing.getClass());
        -		Optional channelDoc = thingDoc.getChannelDoc(channelAddress.getChannelId());
        -		return channelDoc;
        -	}
        -
        -	public synchronized Set getQueryablePersistences() {
        -		return Collections.unmodifiableSet(queryablePersistences);
        -	}
        -
        -	public synchronized Set> getThingClasses() {
        -		return Collections.unmodifiableSet(thingClasses.keySet());
        -	}
        -
        -	public synchronized Set getThingsByClass(Class clazz) {
        -		return Collections.unmodifiableSet(thingClasses.get(clazz));
        -	}
        -
        -	public synchronized Set getThingsAssignableByClass(Class clazz) {
        -		Set things = new HashSet<>();
        -		for (Class subclazz : thingClasses.keySet()) {
        -			if (clazz.isAssignableFrom(subclazz)) {
        -				things.addAll(thingClasses.get(subclazz));
        -			}
        -
        -		}
        -		return Collections.unmodifiableSet(things);
        -	}
        -
        -	public synchronized Optional getThingById(String id) {
        -		return Optional.ofNullable(thingIds.get(id));
        -	}
        -
        -	public synchronized Set getBridges() {
        -		return Collections.unmodifiableSet(bridges);
        -	}
        -
        -	public synchronized Set getSchedulers() {
        -		return Collections.unmodifiableSet(schedulers);
        -	}
        -
        -	public synchronized Set getDeviceNatures() {
        -		return Collections.unmodifiableSet(deviceNatures);
        -	}
        -
        -	public Optional getChannel(String thingId, String channelId) {
        -		Thing thing = thingIds.get(thingId);
        -		if (thing == null) {
        -			return Optional.empty();
        -		}
        -		Map channels = thingChannels.row(thing);
        -		Channel channel = channels.get(channelId);
        -		return Optional.ofNullable(channel);
        -	}
        -
        -	public Optional getChannel(ChannelAddress channelAddress) {
        -		return this.getChannel(channelAddress.getThingId(), channelAddress.getChannelId());
        -	}
        -
        -	public Optional getChannelByAddress(String address) {
        -		try {
        -			return getChannel(ChannelAddress.fromString(address));
        -		} catch (io.openems.common.exceptions.OpenemsException e) {
        -			return Optional.empty();
        -		}
        -	}
        -
        -	public Controller createController(JsonObject jController) throws ReflectionException {
        -		String controllerClass = JsonUtils.getAsString(jController, "class");
        -		Controller controller;
        -		if (jController.has("id")) {
        -			String id = JsonUtils.getAsString(jController, "id");
        -			controller = (Controller) InjectionUtils.getThingInstance(controllerClass, id);
        -		} else {
        -			controller = (Controller) InjectionUtils.getThingInstance(controllerClass);
        -		}
        -		log.info("Add Controller[" + controller.id() + "], Implementation[" + controller.getClass().getSimpleName()
        -				+ "]");
        -		this.addThing(controller);
        -		ConfigUtils.injectConfigChannels(this.getConfigChannels(controller), jController);
        -		return controller;
        -	}
        -
        -	public Device createDevice(JsonObject jDevice, Bridge parent) throws ReflectionException {
        -		String deviceClass = JsonUtils.getAsString(jDevice, "class");
        -		Device device = (Device) InjectionUtils.getThingInstance(deviceClass, parent);
        -		log.info("Add Device[" + device.id() + "], Implementation[" + device.getClass().getSimpleName() + "]");
        -		this.addThing(device);
        -		// instantiate DeviceNatures with Device reference
        -		ConfigUtils.injectConfigChannels(this.getConfigChannels(device), jDevice, device);
        -		return device;
        -	}
        -
        -	@Override
        -	public void thingChannelsUpdated(Thing thing) {
        -		// remove Channels from thingChannels, thingWriteChannels
        -		Databus databus = Databus.getInstance();
        -		Set> thingRow = thingChannels.row(thing).entrySet();
        -		Iterator> i = thingRow.iterator();
        -		while (i.hasNext()) {
        -			Entry thingChannel = i.next();
        -			if (!(thingChannel.getValue() instanceof ConfigChannel)) {
        -				thingChannel.getValue().removeChangeListener(databus);
        -				thingChannel.getValue().removeUpdateListener(databus);
        -				i.remove();
        -			}
        -		}
        -		thingWriteChannels.removeAll(thing);
        -
        -		// Add Channels to thingChannels, thingConfigChannels and thingWriteChannels
        -		ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass());
        -		for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) {
        -			Member member = channelDoc.getMember();
        -			try {
        -				List channels = new ArrayList<>();
        -				boolean ignoreEmpty = false;
        -				if (member instanceof Method) {
        -					if (((Method) member).getReturnType().isArray()) {
        -						ignoreEmpty = true; // ignore e.g. if getFaultChannels is returning an empty array
        -						Channel[] ch = (Channel[]) ((Method) member).invoke(thing);
        -						for (Channel c : ch) {
        -							channels.add(c);
        -						}
        -					} else {
        -						// It's a Method with ReturnType Channel
        -						channels.add((Channel) ((Method) member).invoke(thing));
        -					}
        -				} else if (member instanceof Field) {
        -					// It's a Field with Type Channel
        -					channels.add((Channel) ((Field) member).get(thing));
        -				} else {
        -					continue;
        -				}
        -				if (!ignoreEmpty && channels.isEmpty()) {
        -					log.warn(
        -							"Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]");
        -					continue;
        -				}
        -				for (Channel channel : channels) {
        -					if (channel != null) {
        -						// Add Channel to thingChannels
        -						thingChannels.put(thing, channel.id(), channel);
        -
        -						// Add Channel to writeChannels
        -						if (channel instanceof WriteChannel) {
        -							thingWriteChannels.put(thing, (WriteChannel) channel);
        -						}
        -
        -						// Register Databus as listener
        -						if (channel instanceof ReadChannel) {
        -							((ReadChannel) channel).addUpdateListener(databus);
        -							((ReadChannel) channel).addChangeListener(databus);
        -						}
        -					}
        -				}
        -			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        -				log.warn("Unable to add Channel. Member [" + member.getName() + "]", e);
        -			}
        -		}
        -	}
        -
        -	/**
        -	 * Gets the channel behind Thing member
        -	 *
        -	 * @param thing
        -	 * @param member
        -	 * @return
        -	 * @throws OpenemsException
        -	 */
        -	private Channel getChannel(Thing thing, Member member) throws OpenemsException {
        -		Object channelObj;
        -		if (member instanceof Field) {
        -			Field f = (Field) member;
        -			try {
        -				channelObj = f.get(thing);
        -			} catch (IllegalArgumentException | IllegalAccessException e) {
        -				throw new OpenemsException(
        -						"Unable to get Channel. Thing [" + thing.id() + "] Field [" + f.getName() + "]");
        -			}
        -		} else {
        -			Method m = (Method) member;
        -			try {
        -				channelObj = m.invoke(thing, new Object[0]);
        -			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        -				throw new OpenemsException(
        -						"Unable to get Channel. Thing [" + thing.id() + "] Method [" + m.getName() + "]");
        -			}
        -		}
        -		if (channelObj == null) {
        -			throw new OpenemsException("Channel is null. Thing [" + thing.id() + "] Member [" + member.getName() + "]");
        -		}
        -		if (!(channelObj instanceof Channel)) {
        -			throw new OpenemsException("This is not a channel. Thing [" + thing.id() + "] Member [" + member.getName()
        -			+ "] Channel [" + channelObj + "]");
        -		}
        -		return (Channel) channelObj;
        -	}
        +/*******************************************************************************
        + * 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.core;
        +
        +import java.lang.reflect.Field;
        +import java.lang.reflect.InvocationTargetException;
        +import java.lang.reflect.Member;
        +import java.lang.reflect.Method;
        +import java.util.ArrayList;
        +import java.util.Collection;
        +import java.util.Collections;
        +import java.util.HashSet;
        +import java.util.Iterator;
        +import java.util.LinkedList;
        +import java.util.List;
        +import java.util.Map;
        +import java.util.Map.Entry;
        +import java.util.Optional;
        +import java.util.Set;
        +
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import com.google.common.collect.BiMap;
        +import com.google.common.collect.HashBasedTable;
        +import com.google.common.collect.HashBiMap;
        +import com.google.common.collect.HashMultimap;
        +import com.google.common.collect.Table;
        +import com.google.gson.JsonObject;
        +
        +import io.openems.api.bridge.Bridge;
        +import io.openems.api.channel.Channel;
        +import io.openems.api.channel.ConfigChannel;
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.Controller;
        +import io.openems.api.device.Device;
        +import io.openems.api.device.nature.DeviceNature;
        +import io.openems.api.doc.ChannelDoc;
        +import io.openems.api.doc.ThingDoc;
        +import io.openems.api.persistence.Persistence;
        +import io.openems.api.persistence.QueryablePersistence;
        +import io.openems.api.scheduler.Scheduler;
        +import io.openems.api.thing.Thing;
        +import io.openems.api.thing.ThingChannelsUpdatedListener;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.types.ChannelAddress;
        +import io.openems.common.utils.JsonUtils;
        +import io.openems.core.ThingsChangedListener.Action;
        +import io.openems.core.utilities.ConfigUtils;
        +import io.openems.core.utilities.InjectionUtils;
        +
        +
        +public class ThingRepository implements ThingChannelsUpdatedListener {
        +	private final static Logger log = LoggerFactory.getLogger(ThingRepository.class);
        +
        +	private static ThingRepository instance;
        +
        +	public static synchronized ThingRepository getInstance() {
        +		if (ThingRepository.instance == null) {
        +			ThingRepository.instance = new ThingRepository();
        +		}
        +		return ThingRepository.instance;
        +	}
        +
        +	private ThingRepository() {
        +		classRepository = ClassRepository.getInstance();
        +	}
        +
        +	private final ClassRepository classRepository;
        +	private final BiMap thingIds = HashBiMap.create();
        +	private HashMultimap, Thing> thingClasses = HashMultimap.create();
        +	private Set bridges = new HashSet<>();
        +	// TODO scheduler should not be a set, but only one value
        +	private Set schedulers = new HashSet<>();
        +	private Set persistences = new HashSet<>();
        +	private Set queryablePersistences = new HashSet<>();
        +	private Set deviceNatures = new HashSet<>();
        +	private final Table thingChannels = HashBasedTable.create();
        +	private HashMultimap> thingConfigChannels = HashMultimap.create();
        +	private HashMultimap> thingWriteChannels = HashMultimap.create();
        +	private List thingListeners = new LinkedList<>();
        +
        +	public void addThingChangedListener(ThingsChangedListener listener) {
        +		this.thingListeners.add(listener);
        +	}
        +
        +	public void removeThingChangedListener(ThingsChangedListener listener) {
        +		this.thingListeners.remove(listener);
        +	}
        +
        +	/**
        +	 * Add a Thing to the Repository and cache its Channels and other information for later usage.
        +	 *
        +	 * @param thing
        +	 */
        +	public synchronized void addThing(Thing thing) {
        +		if (thingIds.containsValue(thing)) {
        +			// Thing was already added
        +			return;
        +		}
        +		// Add to thingIds
        +		thingIds.forcePut(thing.id(), thing);
        +
        +		// Add to thingClasses
        +		thingClasses.put(thing.getClass(), thing);
        +
        +		// Add to bridges
        +		if (thing instanceof Bridge) {
        +			bridges.add((Bridge) thing);
        +		}
        +
        +		// Add to schedulers
        +		if (thing instanceof Scheduler) {
        +			schedulers.add((Scheduler) thing);
        +		}
        +
        +		// Add to persistences
        +		if (thing instanceof Persistence) {
        +			persistences.add((Persistence) thing);
        +		}
        +
        +		// Add to queryablePersistences
        +		if (thing instanceof QueryablePersistence) {
        +			queryablePersistences.add((QueryablePersistence) thing);
        +		}
        +
        +		// Add to device natures
        +		if (thing instanceof DeviceNature) {
        +			deviceNatures.add((DeviceNature) thing);
        +		}
        +
        +		// Add Listener
        +		thing.addListener(this);
        +
        +		// Apply channel annotation (this happens now and again after initializing the thing via init()
        +		this.applyChannelAnnotation(thing);
        +
        +		// Add Channels thingConfigChannels
        +		ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass());
        +		for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) {
        +			Member member = channelDoc.getMember();
        +			try {
        +				List channels = new ArrayList<>();
        +				java.util.function.Consumer addToChannels = (c) -> {
        +					if(c == null) {
        +						log.error(
        +								"Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]");
        +					} else {
        +						channels.add(c);
        +					}
        +				};
        +				if (member instanceof Method) {
        +					if (((Method) member).getReturnType().isArray()) {
        +						Channel[] ch = (Channel[]) ((Method) member).invoke(thing);
        +						for (Channel c : ch) {
        +							addToChannels.accept(c);
        +						}
        +					} else {
        +						// It's a Method with ReturnType Channel
        +						Channel c = (Channel) ((Method) member).invoke(thing);
        +						addToChannels.accept(c);
        +					}
        +				} else if (member instanceof Field) {
        +					// It's a Field with Type Channel
        +					Channel c = (Channel) ((Field) member).get(thing);
        +					addToChannels.accept(c);
        +				} else {
        +					continue;
        +				}
        +				if (channels.isEmpty()) {
        +					continue;
        +				}
        +				for (Channel channel : channels) {
        +					// Add Channel to thingChannels
        +					thingChannels.put(thing, channel.id(), channel);
        +					if (channel instanceof ConfigChannel) {
        +
        +						// Add Channel to configChannels
        +						thingConfigChannels.put(thing, (ConfigChannel) channel);
        +					}
        +				}
        +			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        +				log.warn("Unable to add Channel. Member [" + member.getName() + "]", e);
        +			}
        +		}
        +		for (ThingsChangedListener listener : thingListeners) {
        +			listener.thingChanged(thing, Action.ADD);
        +		}
        +	}
        +
        +	public void applyChannelAnnotation(Thing thing) {
        +		ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass());
        +		for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) {
        +			try {
        +				Channel channel = getChannel(thing, channelDoc.getMember());
        +				channel.setChannelDoc(channelDoc);
        +			} catch (OpenemsException e) {
        +				log.debug(e.getMessage());
        +			}
        +		}
        +	}
        +
        +	/**
        +	 * Remove a Thing from the Repository.
        +	 *
        +	 * @param thing
        +	 */
        +	public synchronized void removeThing(String thingId) {
        +		Thing thing = thingIds.get(thingId);
        +		removeThing(thing);
        +	}
        +
        +	/**
        +	 * Remove a Thing from the Repository.
        +	 *
        +	 * @param thing
        +	 */
        +	public synchronized void removeThing(Thing thing) {
        +		// Remove from thingIds
        +		thingIds.remove(thing.id());
        +
        +		// Remove from thingClasses
        +		thingClasses.remove(thing.getClass(), thing);
        +
        +		// Remove from bridges
        +		if (thing instanceof Bridge) {
        +			bridges.remove(thing);
        +		}
        +
        +		// Remove from schedulers
        +		if (thing instanceof Scheduler) {
        +			schedulers.remove(thing);
        +		}
        +
        +		// Remove from persistences
        +		if (thing instanceof Persistence) {
        +			persistences.remove(thing);
        +		}
        +
        +		// Remove from queryablePersistences
        +		if (thing instanceof QueryablePersistence) {
        +			queryablePersistences.remove(thing);
        +		}
        +
        +		// Remove from deviceNatures
        +		if (thing instanceof DeviceNature) {
        +			deviceNatures.remove(thing);
        +		}
        +
        +		// Remove controller
        +		if (thing instanceof Controller) {
        +			Controller controller = (Controller) thing;
        +			for (Scheduler scheduler : getSchedulers()) {
        +				scheduler.removeController(controller);
        +			}
        +		}
        +
        +		// Remove device
        +		if (thing instanceof Device) {
        +			for (Bridge bridge : bridges) {
        +				bridge.removeDevice((Device) thing);
        +			}
        +		}
        +
        +		// Remove Listener
        +		thing.removeListener(this);
        +		// TODO further cleaning if required
        +		for (ThingsChangedListener listener : thingListeners) {
        +			listener.thingChanged(thing, Action.REMOVE);
        +		}
        +	}
        +
        +	public Thing getThing(String thingId) {
        +		Thing thing = thingIds.get(thingId);
        +		return thing;
        +	}
        +
        +	public Set getThings() {
        +		return Collections.unmodifiableSet(this.thingIds.values());
        +	}
        +
        +	/**
        +	 * Returns all Channels for this Thing.
        +	 *
        +	 * @param thing
        +	 * @return
        +	 */
        +	public synchronized Collection getChannels(Thing thing) {
        +		return Collections.unmodifiableCollection(thingChannels.row(thing).values());
        +	}
        +
        +	/**
        +	 * Returns all Config-Channels.
        +	 *
        +	 * @return
        +	 */
        +	public synchronized Collection> getConfigChannels() {
        +		return Collections.unmodifiableCollection(thingConfigChannels.values());
        +	}
        +
        +	/**
        +	 * Returns all Config-Channels for this Thing.
        +	 *
        +	 * @param thing
        +	 * @return
        +	 */
        +	public synchronized Set> getConfigChannels(Thing thing) {
        +		return Collections.unmodifiableSet(thingConfigChannels.get(thing));
        +	}
        +
        +	/**
        +	 * Returns all Write-Channels for this Thing.
        +	 *
        +	 * @param thing
        +	 * @return
        +	 */
        +	public synchronized Set> getWriteChannels(Thing thing) {
        +		return Collections.unmodifiableSet(thingWriteChannels.get(thing));
        +	}
        +
        +	/**
        +	 * Returns all Write-Channels.
        +	 *
        +	 * @param thing
        +	 * @return
        +	 */
        +	public synchronized Collection> getWriteChannels() {
        +		return Collections.unmodifiableCollection(thingWriteChannels.values());
        +	}
        +
        +	/**
        +	 * Returns all Persistence-Workers.
        +	 *
        +	 * @param thing
        +	 * @return
        +	 */
        +	public synchronized Set getPersistences() {
        +		return Collections.unmodifiableSet(persistences);
        +	}
        +
        +	/**
        +	 * Returns the ChannelDoc for a given Channel
        +	 *
        +	 * @param channelAddress
        +	 * @return
        +	 */
        +	public synchronized Optional getChannelDoc(ChannelAddress channelAddress) {
        +		Thing thing = getThing(channelAddress.getThingId());
        +		if (thing == null) {
        +			return Optional.empty();
        +		}
        +		ThingDoc thingDoc = ClassRepository.getInstance().getThingDoc(thing.getClass());
        +		Optional channelDoc = thingDoc.getChannelDoc(channelAddress.getChannelId());
        +		return channelDoc;
        +	}
        +
        +	public synchronized Set getQueryablePersistences() {
        +		return Collections.unmodifiableSet(queryablePersistences);
        +	}
        +
        +	public synchronized Set> getThingClasses() {
        +		return Collections.unmodifiableSet(thingClasses.keySet());
        +	}
        +
        +	public synchronized Set getThingsByClass(Class clazz) {
        +		return Collections.unmodifiableSet(thingClasses.get(clazz));
        +	}
        +
        +	public synchronized Set getThingsAssignableByClass(Class clazz) {
        +		Set things = new HashSet<>();
        +		for (Class subclazz : thingClasses.keySet()) {
        +			if (clazz.isAssignableFrom(subclazz)) {
        +				things.addAll(thingClasses.get(subclazz));
        +			}
        +
        +		}
        +		return Collections.unmodifiableSet(things);
        +	}
        +
        +	public synchronized Optional getThingById(String id) {
        +		return Optional.ofNullable(thingIds.get(id));
        +	}
        +
        +	public synchronized Set getBridges() {
        +		return Collections.unmodifiableSet(bridges);
        +	}
        +
        +	public synchronized Set getSchedulers() {
        +		return Collections.unmodifiableSet(schedulers);
        +	}
        +
        +	public synchronized Set getDeviceNatures() {
        +		return Collections.unmodifiableSet(deviceNatures);
        +	}
        +
        +	public Optional getChannel(String thingId, String channelId) {
        +		Thing thing = thingIds.get(thingId);
        +		if (thing == null) {
        +			return Optional.empty();
        +		}
        +		Map channels = thingChannels.row(thing);
        +		Channel channel = channels.get(channelId);
        +		return Optional.ofNullable(channel);
        +	}
        +
        +	public Optional getChannel(ChannelAddress channelAddress) {
        +		return this.getChannel(channelAddress.getThingId(), channelAddress.getChannelId());
        +	}
        +
        +	public Optional getChannelByAddress(String address) {
        +		try {
        +			return getChannel(ChannelAddress.fromString(address));
        +		} catch (io.openems.common.exceptions.OpenemsException e) {
        +			return Optional.empty();
        +		}
        +	}
        +
        +	public Controller createController(JsonObject jController) throws OpenemsException {
        +		String controllerClass = JsonUtils.getAsString(jController, "class");
        +		Controller controller;
        +		if (jController.has("id")) {
        +			String id = JsonUtils.getAsString(jController, "id");
        +			controller = (Controller) InjectionUtils.getThingInstance(controllerClass, id);
        +		} else {
        +			controller = (Controller) InjectionUtils.getThingInstance(controllerClass);
        +		}
        +		log.info("Add Controller[" + controller.id() + "], Implementation[" + controller.getClass().getSimpleName()
        +				+ "]");
        +		this.addThing(controller);
        +		ConfigUtils.injectConfigChannels(this.getConfigChannels(controller), jController);
        +		return controller;
        +	}
        +
        +	public Device createDevice(JsonObject jDevice, Bridge parent) throws OpenemsException {
        +		String deviceClass = JsonUtils.getAsString(jDevice, "class");
        +		Device device = (Device) InjectionUtils.getThingInstance(deviceClass, parent);
        +		log.info("Add Device[" + device.id() + "], Implementation[" + device.getClass().getSimpleName() + "]");
        +		this.addThing(device);
        +		// instantiate DeviceNatures with Device reference
        +		ConfigUtils.injectConfigChannels(this.getConfigChannels(device), jDevice, device);
        +		return device;
        +	}
        +
        +	@Override
        +	public void thingChannelsUpdated(Thing thing) {
        +		// remove Channels from thingChannels, thingWriteChannels
        +		Databus databus = Databus.getInstance();
        +		Set> thingRow = thingChannels.row(thing).entrySet();
        +		Iterator> i = thingRow.iterator();
        +		while (i.hasNext()) {
        +			Entry thingChannel = i.next();
        +			if (!(thingChannel.getValue() instanceof ConfigChannel)) {
        +				thingChannel.getValue().removeChangeListener(databus);
        +				thingChannel.getValue().removeUpdateListener(databus);
        +				i.remove();
        +			}
        +		}
        +		thingWriteChannels.removeAll(thing);
        +
        +		// Add Channels to thingChannels, thingConfigChannels and thingWriteChannels
        +		ThingDoc thingDoc = classRepository.getThingDoc(thing.getClass());
        +		for (ChannelDoc channelDoc : thingDoc.getChannelDocs()) {
        +			Member member = channelDoc.getMember();
        +			try {
        +				List channels = new ArrayList<>();
        +				boolean ignoreEmpty = false;
        +				if (member instanceof Method) {
        +					if (((Method) member).getReturnType().isArray()) {
        +						ignoreEmpty = true; // ignore e.g. if getFaultChannels is returning an empty array
        +						Channel[] ch = (Channel[]) ((Method) member).invoke(thing);
        +						for (Channel c : ch) {
        +							channels.add(c);
        +						}
        +					} else {
        +						// It's a Method with ReturnType Channel
        +						channels.add((Channel) ((Method) member).invoke(thing));
        +					}
        +				} else if (member instanceof Field) {
        +					// It's a Field with Type Channel
        +					channels.add((Channel) ((Field) member).get(thing));
        +				} else {
        +					continue;
        +				}
        +				if (!ignoreEmpty && channels.isEmpty()) {
        +					log.warn(
        +							"Channel is returning null! Thing [" + thing.id() + "], Member [" + member.getName() + "]");
        +					continue;
        +				}
        +				for (Channel channel : channels) {
        +					if (channel != null) {
        +						// Add Channel to thingChannels
        +						thingChannels.put(thing, channel.id(), channel);
        +
        +						// Add Channel to writeChannels
        +						if (channel instanceof WriteChannel) {
        +							thingWriteChannels.put(thing, (WriteChannel) channel);
        +						}
        +
        +						// Register Databus as listener
        +						if (channel instanceof ReadChannel) {
        +							((ReadChannel) channel).addUpdateListener(databus);
        +							((ReadChannel) channel).addChangeListener(databus);
        +						}
        +					}
        +				}
        +			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        +				log.warn("Unable to add Channel. Member [" + member.getName() + "]", e);
        +			}
        +		}
        +	}
        +
        +	/**
        +	 * Gets the channel behind Thing member
        +	 *
        +	 * @param thing
        +	 * @param member
        +	 * @return
        +	 * @throws OpenemsException
        +	 */
        +	private Channel getChannel(Thing thing, Member member) throws OpenemsException {
        +		Object channelObj;
        +		if (member instanceof Field) {
        +			Field f = (Field) member;
        +			try {
        +				channelObj = f.get(thing);
        +			} catch (IllegalArgumentException | IllegalAccessException e) {
        +				throw new OpenemsException(
        +						"Unable to get Channel. Thing [" + thing.id() + "] Field [" + f.getName() + "]");
        +			}
        +		} else {
        +			Method m = (Method) member;
        +			try {
        +				channelObj = m.invoke(thing, new Object[0]);
        +			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        +				throw new OpenemsException(
        +						"Unable to get Channel. Thing [" + thing.id() + "] Method [" + m.getName() + "]");
        +			}
        +		}
        +		if (channelObj == null) {
        +			throw new OpenemsException("Channel is null. Thing [" + thing.id() + "] Member [" + member.getName() + "]");
        +		}
        +		if (!(channelObj instanceof Channel)) {
        +			throw new OpenemsException("This is not a channel. Thing [" + thing.id() + "] Member [" + member.getName()
        +			+ "] Channel [" + channelObj + "]");
        +		}
        +		return (Channel) channelObj;
        +	}
         }
        \ No newline at end of file
        diff --git a/edge/src/io/openems/core/utilities/BitUtils.java b/edge/src/io/openems/core/utilities/BitUtils.java
        index ac662f1eba2..90020dc60e4 100644
        --- a/edge/src/io/openems/core/utilities/BitUtils.java
        +++ b/edge/src/io/openems/core/utilities/BitUtils.java
        @@ -3,7 +3,7 @@
         import java.nio.ByteBuffer;
         import java.nio.ByteOrder;
         
        -import io.openems.api.exception.NotImplementedException;
        +import io.openems.common.exceptions.NotImplementedException;
         
         public class BitUtils {
         
        diff --git a/edge/src/io/openems/core/utilities/ConfigUtils.java b/edge/src/io/openems/core/utilities/ConfigUtils.java
        index dcac5d6df5f..264372c72a2 100644
        --- a/edge/src/io/openems/core/utilities/ConfigUtils.java
        +++ b/edge/src/io/openems/core/utilities/ConfigUtils.java
        @@ -50,11 +50,12 @@
         import io.openems.api.channel.ConfigChannel;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.Device;
        -import io.openems.api.exception.ConfigException;
        -import io.openems.api.exception.NotImplementedException;
         import io.openems.api.exception.ReflectionException;
         import io.openems.api.thing.Thing;
        +import io.openems.common.exceptions.NotImplementedException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.session.Role;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.ConfigFormat;
         import io.openems.core.ThingRepository;
         
        @@ -66,10 +67,10 @@ public class ConfigUtils {
         	 *
         	 * @param channels
         	 * @param jConfig
        -	 * @throws ConfigException
        +	 * @throws OpenemsException
         	 */
         	public static void injectConfigChannels(Set> channels, JsonObject jConfig, Object... args)
        -			throws ReflectionException {
        +			throws OpenemsException {
         		for (ConfigChannel channel : channels) {
         			if (!jConfig.has(channel.id()) && (channel.valueOptional().isPresent() || channel.isOptional())) {
         				// Element for this Channel is not existing in the configuration, but a default value was set
        @@ -192,10 +193,10 @@ public static JsonElement getAsJsonElement(Object value, ConfigFormat format, Ro
         	 * @param channel
         	 * @param j
         	 * @return
        -	 * @throws ReflectionException
        +	 * @throws OpenemsException
         	 */
         	public static Object getConfigObject(ConfigChannel channel, JsonElement j, Object... args)
        -			throws ReflectionException {
        +			throws OpenemsException {
         		Optional> typeOptional = channel.type();
         		if (!typeOptional.isPresent()) {
         			String clazz = channel.parent() != null ? " in implementation [" + channel.parent().getClass() + "]" : "";
        @@ -244,7 +245,7 @@ public static Object getConfigObject(ConfigChannel channel, JsonElement j, Ob
         	}
         
         	private static Thing getThingFromConfig(Class type, JsonElement j, Object... objects)
        -			throws ReflectionException {
        +			throws OpenemsException {
         		String thingId = JsonUtils.getAsString(j, "id");
         		ThingRepository thingRepository = ThingRepository.getInstance();
         		Optional existingThing = thingRepository.getThingById(thingId);
        diff --git a/edge/src/io/openems/core/utilities/InjectionUtils.java b/edge/src/io/openems/core/utilities/InjectionUtils.java
        index a28e2a29d1f..4d0b30b1cf0 100644
        --- a/edge/src/io/openems/core/utilities/InjectionUtils.java
        +++ b/edge/src/io/openems/core/utilities/InjectionUtils.java
        @@ -1,277 +1,277 @@
        -/*******************************************************************************
        - * 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.core.utilities;
        -
        -import java.lang.reflect.Constructor;
        -import java.lang.reflect.Field;
        -import java.lang.reflect.InvocationTargetException;
        -import java.lang.reflect.ParameterizedType;
        -import java.lang.reflect.Type;
        -import java.util.ArrayList;
        -import java.util.Arrays;
        -import java.util.Collection;
        -import java.util.HashSet;
        -import java.util.List;
        -import java.util.Set;
        -
        -import com.google.gson.JsonArray;
        -import com.google.gson.JsonElement;
        -
        -import io.openems.api.channel.ConfigChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.DeviceNature;
        -import io.openems.api.exception.ConfigException;
        -import io.openems.api.exception.ReflectionException;
        -import io.openems.api.thing.Thing;
        -import io.openems.core.ThingRepository;
        -
        -public class InjectionUtils {
        -	// private final static Logger log = LoggerFactory.getLogger(InjectionUtils.class);
        -
        -	/**
        -	 * Creates an instance of the given {@link Class}. {@link Object} arguments are optional.
        -	 *
        -	 * Restriction: this implementation tries only the first constructor of the Class.
        -	 *
        -	 * @param clazz
        -	 * @param args
        -	 * @return
        -	 * @throws ConfigException
        -	 */
        -	public static Object getInstance(Class clazz, Object... args) throws ReflectionException {
        -		try {
        -			if (args.length == 0) {
        -				return clazz.newInstance();
        -			} else {
        -				Constructor[] constructors = clazz.getConstructors();
        -				Constructor constructor = null;
        -				for (Constructor ct : constructors) {
        -					List> types = new ArrayList<>(Arrays.asList(ct.getParameterTypes()));
        -					if (types.size() == args.length) {
        -						boolean isType = true;
        -						for (Object arg : args) {
        -							boolean isAssignable = false;
        -							for (Class type : types) {
        -								if (type.isAssignableFrom(arg.getClass())) {
        -									isAssignable = true;
        -								}
        -							}
        -							if (!isAssignable) {
        -								isType = false;
        -							}
        -						}
        -						if (isType) {
        -							constructor = ct;
        -						}
        -					}
        -				}
        -				if (constructor != null) {
        -					return constructor.newInstance(args);
        -				} else {
        -					throw new ReflectionException(
        -							"Unable to instantiate class [" + clazz.getName() + "] no matching constructor found.");
        -				}
        -			}
        -		} catch (InstantiationException | IllegalAccessException | InvocationTargetException | IllegalArgumentException
        -				| NullPointerException e) {
        -			e.printStackTrace();
        -			throw new ReflectionException("Unable to instantiate class [" + clazz.getName() + "]: " + e.getMessage());
        -		}
        -	}
        -
        -	/**
        -	 * Creates a Thing instance of the given {@link Class}. {@link Object} arguments are optional.
        -	 *
        -	 * @param clazz
        -	 * @param args
        -	 * @return
        -	 * @throws CastException
        -	 * @throws ConfigException
        -	 * @throws ReflectionException
        -	 */
        -	public static Thing getThingInstance(Class clazz, Object... args) throws ReflectionException {
        -		Thing thing;
        -		try {
        -			thing = (Thing) InjectionUtils.getInstance(clazz, args);
        -		} catch (ClassCastException e) {
        -			e.printStackTrace();
        -			throw new ReflectionException("Class [" + clazz.getName() + "] is not a Thing");
        -		}
        -		return thing;
        -
        -	}
        -
        -	/**
        -	 * Creates an instance of the given {@link Class}name. Uses {@link getThingInstance()} internally. {@link Object}
        -	 * arguments are optional.
        -	 *
        -	 * @param className
        -	 * @return
        -	 * @throws CastException
        -	 * @throws ConfigException
        -	 */
        -	@SuppressWarnings("unchecked")
        -	public static Thing getThingInstance(String className, Object... args) throws ReflectionException {
        -		Class clazz;
        -		try {
        -			clazz = (Class) Class.forName(className);
        -		} catch (ClassNotFoundException e) {
        -			throw new ReflectionException("Class not found: [" + className + "]");
        -		}
        -		return getThingInstance(clazz, args);
        -	}
        -
        -	public static Object getThingMapsFromConfig(ConfigChannel channel, JsonElement j) throws ReflectionException {
        -		/*
        -		 * Get "Field" in Channels parent class
        -		 */
        -		Field field;
        -		try {
        -			field = channel.parent().getClass().getField(channel.id());
        -		} catch (NoSuchFieldException | SecurityException e) {
        -			throw new ReflectionException("Field for ConfigChannel [" + channel.address() + "] is not named ["
        -					+ channel.id() + "] in [" + channel.getClass().getSimpleName() + "]");
        -		}
        -
        -		/*
        -		 * Get expected Object Type (List, Set, simple Object)
        -		 */
        -		Type expectedObjectType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
        -		if (expectedObjectType instanceof ParameterizedType) {
        -			expectedObjectType = ((ParameterizedType) expectedObjectType).getRawType();
        -		}
        -		Class expectedObjectClass = (Class) expectedObjectType;
        -
        -		/*
        -		 * Get the ThingMap class
        -		 */
        -		Class thingMapClass = channel.type().get();
        -
        -		/*
        -		 * Get the referenced Thing class
        -		 */
        -		IsThingMap isThingMapAnnotation = thingMapClass.getAnnotation(IsThingMap.class);
        -		Class thingClass = isThingMapAnnotation.type();
        -
        -		/*
        -		 * Prepare filter for matching Things
        -		 * - Empty filter: accept nothing
        -		 * - Asterisk: accept everything
        -		 * - Otherwise: accept only exact string matches on the thing id
        -		 */
        -		Set filter = new HashSet<>();
        -		if (j.isJsonPrimitive()) {
        -			String id = j.getAsJsonPrimitive().getAsString();
        -			filter.add(id);
        -		} else if (j.isJsonArray()) {
        -			j.getAsJsonArray().forEach(id -> filter.add(id.getAsString()));
        -		}
        -
        -		/*
        -		 * Create ThingMap instance(s) for each matching Thing
        -		 */
        -		ThingRepository thingRepository = ThingRepository.getInstance();
        -		Set matchingThings = thingRepository.getThingsAssignableByClass(thingClass);
        -		Set thingMaps = new HashSet<>();
        -		for (Thing thing : matchingThings) {
        -			if (filter.contains(thing.id()) || filter.contains("*")) {
        -				ThingMap thingMap = (ThingMap) InjectionUtils.getInstance(thingMapClass, thing);
        -				thingMaps.add(thingMap);
        -			}
        -		}
        -
        -		/*
        -		 * Prepare return
        -		 */
        -		if (thingMaps.isEmpty() && !filter.isEmpty()) {
        -			throw new ReflectionException("No matching ThingMap found for ConfigChannel [" + channel.address() + "]");
        -		}
        -
        -		if (Collection.class.isAssignableFrom(expectedObjectClass)) {
        -			if (Set.class.isAssignableFrom(expectedObjectClass)) {
        -				return thingMaps;
        -			} else if (List.class.isAssignableFrom(expectedObjectClass)) {
        -				return new ArrayList<>(thingMaps);
        -			} else {
        -				throw new ReflectionException("Only List and Set ConfigChannels are currently implemented, not ["
        -						+ expectedObjectClass + "]. ConfigChannel [" + channel.address() + "]");
        -			}
        -		} else {
        -			// No collection
        -			if (thingMaps.size() > 1) {
        -				throw new ReflectionException("Field for ConfigChannel [" + channel.address()
        -						+ "] is no collection, but more than one ThingMaps [" + thingMaps + "] is fitting for ["
        -						+ channel.id() + "] in [" + channel.getClass().getSimpleName() + "]");
        -			} else {
        -				return thingMaps.iterator().next();
        -			}
        -		}
        -	}
        -
        -	/**
        -	 * Gets all important nature super interfaces and classes. This data is used by web client to visualize the data
        -	 * appropriately
        -	 *
        -	 * @param clazz
        -	 * @return
        -	 */
        -	public static Set> getImplements(Class clazz) {
        -		Set> ifaces = new HashSet<>();
        -		// stop at certain classes without adding them
        -		if (clazz == null || clazz.equals(Thing.class) || clazz.equals(AbstractWorker.class)) {
        -			return ifaces;
        -		}
        -		// myself
        -		ifaces.add(clazz);
        -		// stop at certain classes WITH adding them
        -		if (clazz.equals(DeviceNature.class)) {
        -			return ifaces;
        -		}
        -		// super interfaces
        -		for (Class iface : clazz.getInterfaces()) {
        -			if (Thing.class.isAssignableFrom(iface)) {
        -				@SuppressWarnings("unchecked") Class thingIface = (Class) iface;
        -				ifaces.addAll(getImplements(thingIface));
        -			}
        -		}
        -		// super classes
        -		Class superclazz = clazz.getSuperclass();
        -		if (superclazz != null && Thing.class.isAssignableFrom(superclazz)) {
        -			@SuppressWarnings("unchecked") Class thingSuperclazz = (Class) superclazz;
        -			ifaces.addAll(getImplements(thingSuperclazz));
        -		}
        -		return ifaces;
        -	}
        -
        -	public static JsonArray getImplementsAsJson(Class clazz) {
        -		JsonArray j = new JsonArray();
        -		for (Class implement : InjectionUtils.getImplements(clazz)) {
        -			if (DeviceNature.class.isAssignableFrom(clazz)) {
        -				// use simple name for DeviceNatures for readability
        -				j.add(implement.getSimpleName());
        -			} else {
        -				j.add(implement.getCanonicalName());
        -			}
        -		}
        -		return j;
        -	}
        -}
        +/*******************************************************************************
        + * 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.core.utilities;
        +
        +import java.lang.reflect.Constructor;
        +import java.lang.reflect.Field;
        +import java.lang.reflect.InvocationTargetException;
        +import java.lang.reflect.ParameterizedType;
        +import java.lang.reflect.Type;
        +import java.util.ArrayList;
        +import java.util.Arrays;
        +import java.util.Collection;
        +import java.util.HashSet;
        +import java.util.List;
        +import java.util.Set;
        +
        +import com.google.gson.JsonArray;
        +import com.google.gson.JsonElement;
        +
        +import io.openems.api.channel.ConfigChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.DeviceNature;
        +import io.openems.api.exception.ConfigException;
        +import io.openems.api.exception.ReflectionException;
        +import io.openems.api.thing.Thing;
        +import io.openems.core.ThingRepository;
        +
        +public class InjectionUtils {
        +	// private final static Logger log = LoggerFactory.getLogger(InjectionUtils.class);
        +
        +	/**
        +	 * Creates an instance of the given {@link Class}. {@link Object} arguments are optional.
        +	 *
        +	 * Restriction: this implementation tries only the first constructor of the Class.
        +	 *
        +	 * @param clazz
        +	 * @param args
        +	 * @return
        +	 * @throws ConfigException
        +	 */
        +	public static Object getInstance(Class clazz, Object... args) throws ReflectionException {
        +		try {
        +			if (args.length == 0) {
        +				return clazz.newInstance();
        +			} else {
        +				Constructor[] constructors = clazz.getConstructors();
        +				Constructor constructor = null;
        +				for (Constructor ct : constructors) {
        +					List> types = new ArrayList<>(Arrays.asList(ct.getParameterTypes()));
        +					if (types.size() == args.length) {
        +						boolean isType = true;
        +						for (Object arg : args) {
        +							boolean isAssignable = false;
        +							for (Class type : types) {
        +								if (type.isAssignableFrom(arg.getClass())) {
        +									isAssignable = true;
        +								}
        +							}
        +							if (!isAssignable) {
        +								isType = false;
        +							}
        +						}
        +						if (isType) {
        +							constructor = ct;
        +						}
        +					}
        +				}
        +				if (constructor != null) {
        +					return constructor.newInstance(args);
        +				} else {
        +					throw new ReflectionException(
        +							"Unable to instantiate class [" + clazz.getName() + "] no matching constructor found.");
        +				}
        +			}
        +		} catch (InstantiationException | IllegalAccessException | InvocationTargetException | IllegalArgumentException
        +				| NullPointerException e) {
        +			e.printStackTrace();
        +			throw new ReflectionException("Unable to instantiate class [" + clazz.getName() + "]: " + e.getMessage());
        +		}
        +	}
        +
        +	/**
        +	 * Creates a Thing instance of the given {@link Class}. {@link Object} arguments are optional.
        +	 *
        +	 * @param clazz
        +	 * @param args
        +	 * @return
        +	 * @throws CastException
        +	 * @throws ConfigException
        +	 * @throws ReflectionException
        +	 */
        +	public static Thing getThingInstance(Class clazz, Object... args) throws ReflectionException {
        +		Thing thing;
        +		try {
        +			thing = (Thing) InjectionUtils.getInstance(clazz, args);
        +		} catch (ClassCastException e) {
        +			e.printStackTrace();
        +			throw new ReflectionException("Class [" + clazz.getName() + "] is not a Thing");
        +		}
        +		return thing;
        +
        +	}
        +
        +	/**
        +	 * Creates an instance of the given {@link Class}name. Uses {@link getThingInstance()} internally. {@link Object}
        +	 * arguments are optional.
        +	 *
        +	 * @param className
        +	 * @return
        +	 * @throws CastException
        +	 * @throws ConfigException
        +	 */
        +	@SuppressWarnings("unchecked")
        +	public static Thing getThingInstance(String className, Object... args) throws ReflectionException {
        +		Class clazz;
        +		try {
        +			clazz = (Class) Class.forName(className);
        +		} catch (ClassNotFoundException e) {
        +			throw new ReflectionException("Class not found: [" + className + "]");
        +		}
        +		return getThingInstance(clazz, args);
        +	}
        +
        +	public static Object getThingMapsFromConfig(ConfigChannel channel, JsonElement j) throws ReflectionException {
        +		/*
        +		 * Get "Field" in Channels parent class
        +		 */
        +		Field field;
        +		try {
        +			field = channel.parent().getClass().getField(channel.id());
        +		} catch (NoSuchFieldException | SecurityException e) {
        +			throw new ReflectionException("Field for ConfigChannel [" + channel.address() + "] is not named ["
        +					+ channel.id() + "] in [" + channel.getClass().getSimpleName() + "]");
        +		}
        +
        +		/*
        +		 * Get expected Object Type (List, Set, simple Object)
        +		 */
        +		Type expectedObjectType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
        +		if (expectedObjectType instanceof ParameterizedType) {
        +			expectedObjectType = ((ParameterizedType) expectedObjectType).getRawType();
        +		}
        +		Class expectedObjectClass = (Class) expectedObjectType;
        +
        +		/*
        +		 * Get the ThingMap class
        +		 */
        +		Class thingMapClass = channel.type().get();
        +
        +		/*
        +		 * Get the referenced Thing class
        +		 */
        +		IsThingMap isThingMapAnnotation = thingMapClass.getAnnotation(IsThingMap.class);
        +		Class thingClass = isThingMapAnnotation.type();
        +
        +		/*
        +		 * Prepare filter for matching Things
        +		 * - Empty filter: accept nothing
        +		 * - Asterisk: accept everything
        +		 * - Otherwise: accept only exact string matches on the thing id
        +		 */
        +		Set filter = new HashSet<>();
        +		if (j.isJsonPrimitive()) {
        +			String id = j.getAsJsonPrimitive().getAsString();
        +			filter.add(id);
        +		} else if (j.isJsonArray()) {
        +			j.getAsJsonArray().forEach(id -> filter.add(id.getAsString()));
        +		}
        +
        +		/*
        +		 * Create ThingMap instance(s) for each matching Thing
        +		 */
        +		ThingRepository thingRepository = ThingRepository.getInstance();
        +		Set matchingThings = thingRepository.getThingsAssignableByClass(thingClass);
        +		Set thingMaps = new HashSet<>();
        +		for (Thing thing : matchingThings) {
        +			if (filter.contains(thing.id()) || filter.contains("*")) {
        +				ThingMap thingMap = (ThingMap) InjectionUtils.getInstance(thingMapClass, thing);
        +				thingMaps.add(thingMap);
        +			}
        +		}
        +
        +		/*
        +		 * Prepare return
        +		 */
        +		if (thingMaps.isEmpty() && !filter.isEmpty()) {
        +			throw new ReflectionException("No matching ThingMap found for ConfigChannel [" + channel.address() + "]");
        +		}
        +
        +		if (Collection.class.isAssignableFrom(expectedObjectClass)) {
        +			if (Set.class.isAssignableFrom(expectedObjectClass)) {
        +				return thingMaps;
        +			} else if (List.class.isAssignableFrom(expectedObjectClass)) {
        +				return new ArrayList<>(thingMaps);
        +			} else {
        +				throw new ReflectionException("Only List and Set ConfigChannels are currently implemented, not ["
        +						+ expectedObjectClass + "]. ConfigChannel [" + channel.address() + "]");
        +			}
        +		} else {
        +			// No collection
        +			if (thingMaps.size() > 1) {
        +				throw new ReflectionException("Field for ConfigChannel [" + channel.address()
        +						+ "] is no collection, but more than one ThingMaps [" + thingMaps + "] is fitting for ["
        +						+ channel.id() + "] in [" + channel.getClass().getSimpleName() + "]");
        +			} else {
        +				return thingMaps.iterator().next();
        +			}
        +		}
        +	}
        +
        +	/**
        +	 * Gets all important nature super interfaces and classes. This data is used by web client to visualize the data
        +	 * appropriately
        +	 *
        +	 * @param clazz
        +	 * @return
        +	 */
        +	public static Set> getImplements(Class clazz) {
        +		Set> ifaces = new HashSet<>();
        +		// stop at certain classes without adding them
        +		if (clazz == null || clazz.equals(Thing.class) || clazz.equals(AbstractWorker.class)) {
        +			return ifaces;
        +		}
        +		// myself
        +		ifaces.add(clazz);
        +		// stop at certain classes WITH adding them
        +		if (clazz.equals(DeviceNature.class)) {
        +			return ifaces;
        +		}
        +		// super interfaces
        +		for (Class iface : clazz.getInterfaces()) {
        +			if (Thing.class.isAssignableFrom(iface)) {
        +				@SuppressWarnings("unchecked") Class thingIface = (Class) iface;
        +				ifaces.addAll(getImplements(thingIface));
        +			}
        +		}
        +		// super classes
        +		Class superclazz = clazz.getSuperclass();
        +		if (superclazz != null && Thing.class.isAssignableFrom(superclazz)) {
        +			@SuppressWarnings("unchecked") Class thingSuperclazz = (Class) superclazz;
        +			ifaces.addAll(getImplements(thingSuperclazz));
        +		}
        +		return ifaces;
        +	}
        +
        +	public static JsonArray getImplementsAsJson(Class clazz) {
        +		JsonArray j = new JsonArray();
        +		for (Class implement : InjectionUtils.getImplements(clazz)) {
        +			if (DeviceNature.class.isAssignableFrom(clazz)) {
        +				// use simple name for DeviceNatures for readability
        +				j.add(implement.getSimpleName());
        +			} else {
        +				j.add(implement.getCanonicalName());
        +			}
        +		}
        +		return j;
        +	}
        +}
        diff --git a/edge/src/io/openems/core/utilities/JsonUtils.java b/edge/src/io/openems/core/utilities/JsonUtils.java
        deleted file mode 100644
        index e2a2be4a390..00000000000
        --- a/edge/src/io/openems/core/utilities/JsonUtils.java
        +++ /dev/null
        @@ -1,326 +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.core.utilities;
        -
        -import java.net.Inet4Address;
        -import java.time.ZoneId;
        -import java.time.ZonedDateTime;
        -import java.util.ArrayList;
        -import java.util.Arrays;
        -import java.util.HashSet;
        -import java.util.List;
        -import java.util.Optional;
        -import java.util.Set;
        -
        -import com.google.gson.Gson;
        -import com.google.gson.GsonBuilder;
        -import com.google.gson.JsonArray;
        -import com.google.gson.JsonElement;
        -import com.google.gson.JsonObject;
        -import com.google.gson.JsonPrimitive;
        -
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.api.exception.ReflectionException;
        -
        -public class JsonUtils {
        -	public static JsonArray getAsJsonArray(JsonElement jElement) throws ReflectionException {
        -		if (!jElement.isJsonArray()) {
        -			throw new ReflectionException("Config is not a JsonArray: " + jElement);
        -		}
        -		return jElement.getAsJsonArray();
        -	};
        -
        -	public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonElement jSubElement = getSubElement(jElement, memberName);
        -		if (!jSubElement.isJsonArray()) {
        -			throw new ReflectionException("Config [" + memberName + "] is not a JsonArray: " + jSubElement);
        -		}
        -		return jSubElement.getAsJsonArray();
        -	};
        -
        -	public static JsonObject getAsJsonObject(JsonElement jElement) throws ReflectionException {
        -		if (!jElement.isJsonObject()) {
        -			throw new ReflectionException("Config is not a JsonObject: " + jElement);
        -		}
        -		return jElement.getAsJsonObject();
        -	};
        -
        -	public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonElement jsubElement = getSubElement(jElement, memberName);
        -		if (!jsubElement.isJsonObject()) {
        -			throw new ReflectionException("Config is not a JsonObject: " + jsubElement);
        -		}
        -		return jsubElement.getAsJsonObject();
        -	};
        -
        -	public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws ReflectionException {
        -		if (!jElement.isJsonPrimitive()) {
        -			throw new ReflectionException("Config is not a JsonPrimitive: " + jElement);
        -		}
        -		return jElement.getAsJsonPrimitive();
        -	}
        -
        -	public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonElement jSubElement = getSubElement(jElement, memberName);
        -		if (!jSubElement.isJsonPrimitive()) {
        -			throw new ReflectionException("Config is not a JsonPrimitive: " + jSubElement);
        -		}
        -		return jSubElement.getAsJsonPrimitive();
        -	}
        -
        -	public static String getAsString(JsonElement jElement) throws ReflectionException {
        -		JsonPrimitive jPrimitive = getAsPrimitive(jElement);
        -		if (!jPrimitive.isString()) {
        -			throw new ReflectionException("Config is not a String: " + jPrimitive);
        -		}
        -		return jPrimitive.getAsString();
        -	}
        -
        -	public static String getAsString(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName);
        -		if (!jPrimitive.isString()) {
        -			throw new ReflectionException("[" + memberName + "] is not a String: " + jPrimitive);
        -		}
        -		return jPrimitive.getAsString();
        -	}
        -
        -	public static int getAsInt(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName);
        -		if (jPrimitive.isNumber()) {
        -			return jPrimitive.getAsInt();
        -		} else if (jPrimitive.isString()) {
        -			String string = jPrimitive.getAsString();
        -			return Integer.parseInt(string);
        -		}
        -		throw new ReflectionException("[" + memberName + "] is not a Number: " + jPrimitive);
        -	}
        -
        -	public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName, ZoneId timezone)
        -			throws ReflectionException {
        -		String[] date = JsonUtils.getAsString(jElement, memberName).split("-");
        -		try {
        -			int year = Integer.valueOf(date[0]);
        -			int month = Integer.valueOf(date[1]);
        -			int day = Integer.valueOf(date[2]);
        -			return ZonedDateTime.of(year, month, day, 0, 0, 0, 0, timezone);
        -		} catch (ArrayIndexOutOfBoundsException e) {
        -			throw new ReflectionException("Unable to parse date [" + memberName + "] from [" + jElement + "]: " + e);
        -		}
        -	}
        -
        -	public static long getAsLong(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName);
        -		if (jPrimitive.isNumber()) {
        -			return jPrimitive.getAsLong();
        -		} else if (jPrimitive.isString()) {
        -			String string = jPrimitive.getAsString();
        -			return Long.parseLong(string);
        -		}
        -		throw new ReflectionException("[" + memberName + "] is not a Number: " + jPrimitive);
        -	}
        -
        -	public static boolean getAsBoolean(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName);
        -		if (jPrimitive.isBoolean()) {
        -			return jPrimitive.getAsBoolean();
        -		}
        -		throw new ReflectionException("[" + memberName + "] is not a Boolean: " + jPrimitive);
        -	}
        -
        -	public static JsonElement getSubElement(JsonElement jElement, String memberName) throws ReflectionException {
        -		JsonObject jObject = getAsJsonObject(jElement);
        -		if (!jObject.has(memberName)) {
        -			throw new ReflectionException("[" + memberName + "] is missing: " + jElement);
        -		}
        -		return jObject.get(memberName);
        -	}
        -
        -	public static JsonElement getAsJsonElement(Object value) throws NotImplementedException {
        -		// null
        -		if (value == null) {
        -			return null;
        -		}
        -		// optional
        -		if (value instanceof Optional) {
        -			if (!((Optional) value).isPresent()) {
        -				return null;
        -			} else {
        -				value = ((Optional) value).get();
        -			}
        -		}
        -		if (value instanceof Number) {
        -			/*
        -			 * Number
        -			 */
        -			return new JsonPrimitive((Number) value);
        -		} else if (value instanceof String) {
        -			/*
        -			 * String
        -			 */
        -			return new JsonPrimitive((String) value);
        -		} else if (value instanceof Boolean) {
        -			/*
        -			 * Boolean
        -			 */
        -			return new JsonPrimitive((Boolean) value);
        -		} else if (value instanceof Inet4Address) {
        -			/*
        -			 * Inet4Address
        -			 */
        -			return new JsonPrimitive(((Inet4Address) value).getHostAddress());
        -		} else if (value instanceof JsonElement) {
        -			/*
        -			 * JsonElement
        -			 */
        -			return (JsonElement) value;
        -		} else if (value instanceof Long[]){
        -			/*
        -			 * Long-Array
        -			 */
        -			JsonArray js = new JsonArray();
        -			for (Long l : (Long[]) value){
        -				js.add(new JsonPrimitive((Long) l));
        -			}
        -			return js;
        -		}
        -		throw new NotImplementedException("Converter for [" + value + "]" + " of type [" //
        -				+ value.getClass().getSimpleName() + "]" //
        -				+ " to JSON is not implemented.");
        -	}
        -
        -	public static Object getAsType(Optional> typeOptional, JsonElement j) throws NotImplementedException {
        -		if (!typeOptional.isPresent()) {
        -			throw new NotImplementedException("Type of Channel was not set: " + j.getAsString());
        -		}
        -		Class type = typeOptional.get();
        -		return getAsType(type, j);
        -	}
        -
        -	public static Object getAsType(Class type, JsonElement j) throws NotImplementedException {
        -		try {
        -			if (Integer.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for an Integer
        -				 */
        -				return j.getAsInt();
        -
        -			} else if (Long.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for an Long
        -				 */
        -				return j.getAsLong();
        -			} else if (Boolean.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for an Boolean
        -				 */
        -				return j.getAsBoolean();
        -			} else if (Double.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for an Double
        -				 */
        -				return j.getAsDouble();
        -			} else if (String.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for a String
        -				 */
        -				return j.getAsString();
        -			} else if (JsonObject.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for a JsonObject
        -				 */
        -				return j.getAsJsonObject();
        -			} else if (JsonArray.class.isAssignableFrom(type)) {
        -				/*
        -				 * Asking for a JsonArray
        -				 */
        -				return j.getAsJsonArray();
        -			} else if (type.isArray()){
        -				/**
        -				 * Asking for Array
        -				 */
        -				if(Long.class.isAssignableFrom(type.getComponentType())){
        -					/**
        -					 * Asking for ArrayOfLong
        -					 */
        -					if(j.isJsonArray()){
        -						JsonArray js = j.getAsJsonArray();
        -						Long[] la = new Long[js.size()];
        -						for(int i = 0; i < js.size(); i++){
        -							la[i] = js.get(i).getAsLong();
        -						}
        -						return la;
        -					}
        -
        -				}
        -			}
        -		} catch (IllegalStateException e) {
        -			throw new IllegalStateException("Failed to parse JsonElement [" + j + "]", e);
        -		}
        -		throw new NotImplementedException(
        -				"Converter for value [" + j + "] to class type [" + type + "] is not implemented.");
        -	}
        -
        -	public static boolean hasElement(JsonElement j, String... paths) {
        -		return getMatchingElements(j, paths).size() > 0;
        -	}
        -
        -	public static Set getMatchingElements(JsonElement j, String... paths) {
        -		Set result = new HashSet();
        -		if (paths.length == 0) {
        -			// last path element
        -			result.add(j);
        -			return result;
        -		}
        -		String path = paths[0];
        -		if (j.isJsonObject()) {
        -			JsonObject jO = j.getAsJsonObject();
        -			if (jO.has(path)) {
        -				List nextPathsList = new ArrayList(Arrays.asList(paths));
        -				nextPathsList.remove(0);
        -				String[] nextPaths = nextPathsList.toArray(new String[0]);
        -				result.addAll(getMatchingElements(jO.get(path), nextPaths));
        -			}
        -		} else if (j.isJsonArray()) {
        -			for (JsonElement jE : j.getAsJsonArray()) {
        -				result.addAll(getMatchingElements(jE, paths));
        -			}
        -		} else if (j.isJsonPrimitive()) {
        -			JsonPrimitive jP = j.getAsJsonPrimitive();
        -			if (jP.isString()) {
        -				if (jP.getAsString().equals(path)) {
        -					result.add(jP);
        -				}
        -			}
        -		}
        -		return result;
        -	}
        -
        -	/**
        -	 * Pretty print a JsonElement
        -	 *
        -	 * @param j
        -	 */
        -	public static void prettyPrint(JsonElement j) {
        -		Gson gson = new GsonBuilder().setPrettyPrinting().create();
        -		String json = gson.toJson(j);
        -		System.out.println(json);
        -	}
        -}
        diff --git a/edge/src/io/openems/core/utilities/OpenemsTypes.java b/edge/src/io/openems/core/utilities/OpenemsTypes.java
        index eea06aa8ab2..14b45965546 100644
        --- a/edge/src/io/openems/core/utilities/OpenemsTypes.java
        +++ b/edge/src/io/openems/core/utilities/OpenemsTypes.java
        @@ -7,7 +7,7 @@
         
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.DeviceNature;
        -import io.openems.api.exception.NotImplementedException;
        +import io.openems.common.exceptions.NotImplementedException;
         import io.openems.common.types.ChannelEnum;
         
         /**
        diff --git a/edge/src/io/openems/core/utilities/SymmetricPower.java b/edge/src/io/openems/core/utilities/SymmetricPower.java
        index e90c4d7d19d..e4169a088c0 100644
        --- a/edge/src/io/openems/core/utilities/SymmetricPower.java
        +++ b/edge/src/io/openems/core/utilities/SymmetricPower.java
        @@ -1,305 +1,305 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.core.utilities;
        -
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.exception.WriteChannelException;
        -
        -/**
        - * Helper class to reduce and set power to the ess
        - *
        - * @author matthias.rossmann
        - *
        - */
        -public class SymmetricPower {
        -	protected final Logger log = LoggerFactory.getLogger(this.getClass());
        -
        -	private final ReadChannel allowedDischarge;
        -	private final ReadChannel allowedCharge;
        -	private final ReadChannel allowedApparent;
        -	private final WriteChannel setActivePower;
        -	private final WriteChannel setReactivePower;
        -	private long activePower = 0L;
        -	private long reactivePower = 0L;
        -	private boolean activePowerValid = false;
        -	private boolean reactivePowerValid = false;
        -
        -	public SymmetricPower(ReadChannel allowedDischarge, ReadChannel allowedCharge,
        -			ReadChannel allowedApparent, WriteChannel setActivePower, WriteChannel setReactivePower) {
        -		super();
        -		this.allowedDischarge = allowedDischarge;
        -		this.allowedCharge = allowedCharge;
        -		this.allowedApparent = allowedApparent;
        -		this.setActivePower = setActivePower;
        -		this.setReactivePower = setReactivePower;
        -	}
        -
        -	public void setActivePower(long power) {
        -		this.activePower = power;
        -		this.activePowerValid = true;
        -	}
        -
        -	public void setReactivePower(long power) {
        -		this.reactivePower = power;
        -		this.reactivePowerValid = true;
        -	}
        -
        -	public long getActivePower() {
        -		return this.activePower;
        -	}
        -
        -	public long getReactivePower() {
        -		return this.reactivePower;
        -	}
        -
        -	/**
        -	 * Reduces the active and reactive power to the power limitations
        -	 */
        -	public void reducePower() {
        -		// get Min/Max values
        -		long minActivePower = getMinActivePower();
        -		long maxActivePower = getMaxActivePower();
        -		long minReactivePower = getMinReactivePower();
        -		long maxReactivePower = getMaxReactivePower();
        -
        -		// variables for reducedPower
        -		long reducedActivePower = 0L;
        -		long reducedReactivePower = 0L;
        -
        -		// Check if active power is already set
        -		if (setActivePower.getWriteValue().isPresent()) {
        -			this.activePower = setActivePower.peekWrite().get();
        -		}
        -		// Check if reactive power is already set
        -		if (setReactivePower.getWriteValue().isPresent()) {
        -			this.reactivePower = setReactivePower.peekWrite().get();
        -		}
        -
        -		// calculate cosPhi
        -		double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower);
        -
        -		if (minActivePower <= activePower && activePower <= maxActivePower && minReactivePower <= reactivePower
        -				&& reactivePower <= maxReactivePower) {
        -			// activePower and reactivePower are in allowed value range
        -			// no need to reduce power
        -			reducedActivePower = activePower;
        -			reducedReactivePower = reactivePower;
        -		} else if ((minActivePower > activePower || activePower > maxActivePower)
        -				&& (minReactivePower > reactivePower || reactivePower > maxReactivePower)) {
        -			// activePower and reactivePower are out of allowed value range
        -			long reducedActivePower1 = 0L;
        -			long reducedActivePower2 = 0L;
        -			long reducedReactivePower1 = 0L;
        -			long reducedReactivePower2 = 0L;
        -			if (!ControllerUtils.isCharge(activePower, reactivePower)) {
        -				// Discharge
        -				reducedActivePower1 = minActivePower;
        -				reducedReactivePower1 = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        -				reducedReactivePower2 = minReactivePower;
        -				reducedActivePower2 = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower2,
        -						cosPhi);
        -			} else {
        -				// Charge
        -				reducedActivePower1 = maxActivePower;
        -				reducedReactivePower1 = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        -				reducedReactivePower2 = maxReactivePower;
        -				reducedActivePower2 = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower2,
        -						cosPhi);
        -			}
        -			// get largest fitting active and reactive power for min max values
        -			if (ControllerUtils.calculateApparentPower(reducedActivePower1, reducedReactivePower1) > ControllerUtils
        -					.calculateApparentPower(reducedActivePower2, reducedReactivePower2)
        -					&& minActivePower <= reducedActivePower1 && reducedActivePower1 <= maxActivePower
        -					&& minReactivePower <= reducedReactivePower1 && reducedReactivePower1 <= maxReactivePower) {
        -				reducedActivePower = reducedActivePower1;
        -				reducedReactivePower = reducedReactivePower1;
        -			} else if (minActivePower <= reducedActivePower2 && reducedActivePower2 <= maxActivePower
        -					&& minReactivePower <= reducedReactivePower2 && reducedReactivePower2 <= maxReactivePower) {
        -				reducedActivePower = reducedActivePower2;
        -				reducedReactivePower = reducedReactivePower2;
        -			} else {
        -				log.error("Can't reduce power to fit the power limitations!");
        -			}
        -		} else if (minActivePower > activePower || activePower > maxActivePower) {
        -			// only activePower is out of allowed value range
        -			if (minActivePower > activePower) {
        -				// Discharge
        -				reducedActivePower = minActivePower;
        -				reducedReactivePower = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        -			} else {
        -				// Charge
        -				reducedActivePower = maxActivePower;
        -				reducedReactivePower = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        -			}
        -		} else {
        -			// only reactivePower is out of allowed value range
        -			if (minReactivePower > reactivePower) {
        -				// Discharge
        -				reducedReactivePower = minReactivePower;
        -				reducedActivePower = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower,
        -						cosPhi);
        -			} else {
        -				// Charge
        -				reducedReactivePower = maxReactivePower;
        -				reducedActivePower = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower,
        -						cosPhi);
        -			}
        -		}
        -		if (activePower != reducedActivePower || reactivePower != reducedReactivePower) {
        -			log.info("Reduce activePower from [{}] to [{}] and reactivePower from [{}] to [{}]",
        -					new Object[] { activePower, reducedActivePower, reactivePower, reducedReactivePower });
        -		}
        -		this.activePower = reducedActivePower;
        -		this.reactivePower = reducedReactivePower;
        -	}
        -
        -	/**
        -	 * Writes active and reactive power to the setActive-/setReactivePower Channel if the value was set
        -	 */
        -	public void writePower() {
        -		this.reducePower();
        -		try {
        -			// activePowerQueue.add(activePower);
        -			if (activePowerValid) {
        -				setActivePower.pushWrite(activePower);
        -			}
        -			// reactivePowerQueue.add(reactivePower);
        -			if (reactivePowerValid) {
        -				setReactivePower.pushWrite(reactivePower);
        -			}
        -		} catch (WriteChannelException e) {
        -			log.error("Failed to reduce and set Power!");
        -		}
        -		activePowerValid = false;
        -		reactivePowerValid = false;
        -		activePower = 0L;
        -		reactivePower = 0L;
        -	}
        -
        -	/**
        -	 * Get the max active power out of allowedDischarge, allowedApparent and writeMax of setActivePower channels
        -	 *
        -	 * @return max allowed activePower
        -	 */
        -	public long getMaxActivePower() {
        -		long maxPower = 0;
        -		boolean valid = false;
        -		if (allowedDischarge.valueOptional().isPresent()) {
        -			maxPower = allowedDischarge.valueOptional().get();
        -			valid = true;
        -		}
        -		if (valid && allowedApparent.valueOptional().isPresent()) {
        -			maxPower = Math.min(maxPower, allowedApparent.valueOptional().get());
        -		} else if (allowedApparent.valueOptional().isPresent()) {
        -			maxPower = allowedApparent.valueOptional().get();
        -			valid = true;
        -		}
        -		if (valid && setActivePower.writeMax().isPresent()) {
        -			maxPower = Math.min(maxPower, setActivePower.writeMax().get());
        -		} else if (setActivePower.writeMax().isPresent()) {
        -			maxPower = setActivePower.writeMax().get();
        -		}
        -		if (!valid) {
        -			log.error("Failed to get Max value for ActivePower! Return 0.");
        -		}
        -		return maxPower;
        -	}
        -
        -	/**
        -	 * Get the min active power out of allowedCharge, allowedApparent and writeMin of setActivePower channels
        -	 *
        -	 * @return min allowed activePower
        -	 */
        -	public long getMinActivePower() {
        -		long minPower = 0;
        -		boolean valid = false;
        -		if (allowedCharge.valueOptional().isPresent()) {
        -			minPower = allowedCharge.valueOptional().get();
        -			valid = true;
        -		}
        -		if (valid && allowedApparent.valueOptional().isPresent()) {
        -			minPower = Math.max(minPower, allowedApparent.valueOptional().get() * -1);
        -		} else if (allowedApparent.valueOptional().isPresent()) {
        -			minPower = allowedApparent.valueOptional().get() * -1;
        -			valid = true;
        -		}
        -		if (valid && setActivePower.writeMin().isPresent()) {
        -			minPower = Math.max(minPower, setActivePower.writeMin().get());
        -		} else if (setActivePower.writeMin().isPresent()) {
        -			minPower = setActivePower.writeMin().get();
        -		}
        -		if (!valid) {
        -			log.error("Failed to get Min value for ActivePower! Return 0.");
        -		}
        -		return minPower;
        -	}
        -
        -	/**
        -	 * Get the max reactive power out of allowedDischarge, allowedApparent and writeMax of setReactivePower channels
        -	 *
        -	 * @return max allowed reactivePower
        -	 */
        -	public long getMaxReactivePower() {
        -		long maxPower = 0;
        -		boolean valid = false;
        -		if (allowedApparent.valueOptional().isPresent()) {
        -			maxPower = allowedApparent.valueOptional().get();
        -			valid = true;
        -		}
        -		if (valid && setReactivePower.writeMax().isPresent()) {
        -			maxPower = Math.min(maxPower, setReactivePower.writeMax().get());
        -		} else if (setReactivePower.writeMax().isPresent()) {
        -			maxPower = setReactivePower.writeMax().get();
        -		}
        -		if (!valid) {
        -			log.debug("Failed to get Max value for ReactivePower! Return 0.");
        -		}
        -		return maxPower;
        -	}
        -
        -	/**
        -	 * Get the min reactive power out of allowedCharge, allowedApparent and writeMin of setReactivePower channels
        -	 *
        -	 * @return min allowed reactivePower
        -	 */
        -	public long getMinReactivePower() {
        -		long minPower = 0;
        -		boolean valid = false;
        -		if (allowedApparent.valueOptional().isPresent()) {
        -			minPower = allowedApparent.valueOptional().get() * -1;
        -			valid = true;
        -		}
        -		if (valid && setReactivePower.writeMin().isPresent()) {
        -			minPower = Math.max(minPower, setReactivePower.writeMin().get());
        -		} else if (setReactivePower.writeMin().isPresent()) {
        -			minPower = setReactivePower.writeMin().get();
        -		}
        -		if (!valid) {
        -			log.debug("Failed to get Min value for ReactivePower! Return 0.");
        -		}
        -		return minPower;
        -	}
        -
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.core.utilities;
        +
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.exception.WriteChannelException;
        +
        +/**
        + * Helper class to reduce and set power to the ess
        + *
        + * @author matthias.rossmann
        + *
        + */
        +public class SymmetricPower {
        +	protected final Logger log = LoggerFactory.getLogger(this.getClass());
        +
        +	private final ReadChannel allowedDischarge;
        +	private final ReadChannel allowedCharge;
        +	private final ReadChannel allowedApparent;
        +	private final WriteChannel setActivePower;
        +	private final WriteChannel setReactivePower;
        +	private long activePower = 0L;
        +	private long reactivePower = 0L;
        +	private boolean activePowerValid = false;
        +	private boolean reactivePowerValid = false;
        +
        +	public SymmetricPower(ReadChannel allowedDischarge, ReadChannel allowedCharge,
        +			ReadChannel allowedApparent, WriteChannel setActivePower, WriteChannel setReactivePower) {
        +		super();
        +		this.allowedDischarge = allowedDischarge;
        +		this.allowedCharge = allowedCharge;
        +		this.allowedApparent = allowedApparent;
        +		this.setActivePower = setActivePower;
        +		this.setReactivePower = setReactivePower;
        +	}
        +
        +	public void setActivePower(long power) {
        +		this.activePower = power;
        +		this.activePowerValid = true;
        +	}
        +
        +	public void setReactivePower(long power) {
        +		this.reactivePower = power;
        +		this.reactivePowerValid = true;
        +	}
        +
        +	public long getActivePower() {
        +		return this.activePower;
        +	}
        +
        +	public long getReactivePower() {
        +		return this.reactivePower;
        +	}
        +
        +	/**
        +	 * Reduces the active and reactive power to the power limitations
        +	 */
        +	public void reducePower() {
        +		// get Min/Max values
        +		long minActivePower = getMinActivePower();
        +		long maxActivePower = getMaxActivePower();
        +		long minReactivePower = getMinReactivePower();
        +		long maxReactivePower = getMaxReactivePower();
        +
        +		// variables for reducedPower
        +		long reducedActivePower = 0L;
        +		long reducedReactivePower = 0L;
        +
        +		// Check if active power is already set
        +		if (setActivePower.getWriteValue().isPresent()) {
        +			this.activePower = setActivePower.peekWrite().get();
        +		}
        +		// Check if reactive power is already set
        +		if (setReactivePower.getWriteValue().isPresent()) {
        +			this.reactivePower = setReactivePower.peekWrite().get();
        +		}
        +
        +		// calculate cosPhi
        +		double cosPhi = ControllerUtils.calculateCosPhi(activePower, reactivePower);
        +
        +		if (minActivePower <= activePower && activePower <= maxActivePower && minReactivePower <= reactivePower
        +				&& reactivePower <= maxReactivePower) {
        +			// activePower and reactivePower are in allowed value range
        +			// no need to reduce power
        +			reducedActivePower = activePower;
        +			reducedReactivePower = reactivePower;
        +		} else if ((minActivePower > activePower || activePower > maxActivePower)
        +				&& (minReactivePower > reactivePower || reactivePower > maxReactivePower)) {
        +			// activePower and reactivePower are out of allowed value range
        +			long reducedActivePower1 = 0L;
        +			long reducedActivePower2 = 0L;
        +			long reducedReactivePower1 = 0L;
        +			long reducedReactivePower2 = 0L;
        +			if (!ControllerUtils.isCharge(activePower, reactivePower)) {
        +				// Discharge
        +				reducedActivePower1 = minActivePower;
        +				reducedReactivePower1 = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        +				reducedReactivePower2 = minReactivePower;
        +				reducedActivePower2 = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower2,
        +						cosPhi);
        +			} else {
        +				// Charge
        +				reducedActivePower1 = maxActivePower;
        +				reducedReactivePower1 = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        +				reducedReactivePower2 = maxReactivePower;
        +				reducedActivePower2 = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower2,
        +						cosPhi);
        +			}
        +			// get largest fitting active and reactive power for min max values
        +			if (ControllerUtils.calculateApparentPower(reducedActivePower1, reducedReactivePower1) > ControllerUtils
        +					.calculateApparentPower(reducedActivePower2, reducedReactivePower2)
        +					&& minActivePower <= reducedActivePower1 && reducedActivePower1 <= maxActivePower
        +					&& minReactivePower <= reducedReactivePower1 && reducedReactivePower1 <= maxReactivePower) {
        +				reducedActivePower = reducedActivePower1;
        +				reducedReactivePower = reducedReactivePower1;
        +			} else if (minActivePower <= reducedActivePower2 && reducedActivePower2 <= maxActivePower
        +					&& minReactivePower <= reducedReactivePower2 && reducedReactivePower2 <= maxReactivePower) {
        +				reducedActivePower = reducedActivePower2;
        +				reducedReactivePower = reducedReactivePower2;
        +			} else {
        +				log.error("Can't reduce power to fit the power limitations!");
        +			}
        +		} else if (minActivePower > activePower || activePower > maxActivePower) {
        +			// only activePower is out of allowed value range
        +			if (minActivePower > activePower) {
        +				// Discharge
        +				reducedActivePower = minActivePower;
        +				reducedReactivePower = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        +			} else {
        +				// Charge
        +				reducedActivePower = maxActivePower;
        +				reducedReactivePower = ControllerUtils.calculateReactivePower(reducedActivePower, cosPhi);
        +			}
        +		} else {
        +			// only reactivePower is out of allowed value range
        +			if (minReactivePower > reactivePower) {
        +				// Discharge
        +				reducedReactivePower = minReactivePower;
        +				reducedActivePower = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower,
        +						cosPhi);
        +			} else {
        +				// Charge
        +				reducedReactivePower = maxReactivePower;
        +				reducedActivePower = ControllerUtils.calculateActivePowerFromReactivePower(reducedReactivePower,
        +						cosPhi);
        +			}
        +		}
        +		if (activePower != reducedActivePower || reactivePower != reducedReactivePower) {
        +			log.info("Reduce activePower from [{}] to [{}] and reactivePower from [{}] to [{}]",
        +					new Object[] { activePower, reducedActivePower, reactivePower, reducedReactivePower });
        +		}
        +		this.activePower = reducedActivePower;
        +		this.reactivePower = reducedReactivePower;
        +	}
        +
        +	/**
        +	 * Writes active and reactive power to the setActive-/setReactivePower Channel if the value was set
        +	 */
        +	public void writePower() {
        +		this.reducePower();
        +		try {
        +			// activePowerQueue.add(activePower);
        +			if (activePowerValid) {
        +				setActivePower.pushWrite(activePower);
        +			}
        +			// reactivePowerQueue.add(reactivePower);
        +			if (reactivePowerValid) {
        +				setReactivePower.pushWrite(reactivePower);
        +			}
        +		} catch (WriteChannelException e) {
        +			log.error("Failed to reduce and set Power!");
        +		}
        +		activePowerValid = false;
        +		reactivePowerValid = false;
        +		activePower = 0L;
        +		reactivePower = 0L;
        +	}
        +
        +	/**
        +	 * Get the max active power out of allowedDischarge, allowedApparent and writeMax of setActivePower channels
        +	 *
        +	 * @return max allowed activePower
        +	 */
        +	public long getMaxActivePower() {
        +		long maxPower = 0;
        +		boolean valid = false;
        +		if (allowedDischarge.valueOptional().isPresent()) {
        +			maxPower = allowedDischarge.valueOptional().get();
        +			valid = true;
        +		}
        +		if (valid && allowedApparent.valueOptional().isPresent()) {
        +			maxPower = Math.min(maxPower, allowedApparent.valueOptional().get());
        +		} else if (allowedApparent.valueOptional().isPresent()) {
        +			maxPower = allowedApparent.valueOptional().get();
        +			valid = true;
        +		}
        +		if (valid && setActivePower.writeMax().isPresent()) {
        +			maxPower = Math.min(maxPower, setActivePower.writeMax().get());
        +		} else if (setActivePower.writeMax().isPresent()) {
        +			maxPower = setActivePower.writeMax().get();
        +		}
        +		if (!valid) {
        +			log.error("Failed to get Max value for ActivePower! Return 0.");
        +		}
        +		return maxPower;
        +	}
        +
        +	/**
        +	 * Get the min active power out of allowedCharge, allowedApparent and writeMin of setActivePower channels
        +	 *
        +	 * @return min allowed activePower
        +	 */
        +	public long getMinActivePower() {
        +		long minPower = 0;
        +		boolean valid = false;
        +		if (allowedCharge.valueOptional().isPresent()) {
        +			minPower = allowedCharge.valueOptional().get();
        +			valid = true;
        +		}
        +		if (valid && allowedApparent.valueOptional().isPresent()) {
        +			minPower = Math.max(minPower, allowedApparent.valueOptional().get() * -1);
        +		} else if (allowedApparent.valueOptional().isPresent()) {
        +			minPower = allowedApparent.valueOptional().get() * -1;
        +			valid = true;
        +		}
        +		if (valid && setActivePower.writeMin().isPresent()) {
        +			minPower = Math.max(minPower, setActivePower.writeMin().get());
        +		} else if (setActivePower.writeMin().isPresent()) {
        +			minPower = setActivePower.writeMin().get();
        +		}
        +		if (!valid) {
        +			log.error("Failed to get Min value for ActivePower! Return 0.");
        +		}
        +		return minPower;
        +	}
        +
        +	/**
        +	 * Get the max reactive power out of allowedDischarge, allowedApparent and writeMax of setReactivePower channels
        +	 *
        +	 * @return max allowed reactivePower
        +	 */
        +	public long getMaxReactivePower() {
        +		long maxPower = 0;
        +		boolean valid = false;
        +		if (allowedApparent.valueOptional().isPresent()) {
        +			maxPower = allowedApparent.valueOptional().get();
        +			valid = true;
        +		}
        +		if (valid && setReactivePower.writeMax().isPresent()) {
        +			maxPower = Math.min(maxPower, setReactivePower.writeMax().get());
        +		} else if (setReactivePower.writeMax().isPresent()) {
        +			maxPower = setReactivePower.writeMax().get();
        +		}
        +		if (!valid) {
        +			log.debug("Failed to get Max value for ReactivePower! Return 0.");
        +		}
        +		return maxPower;
        +	}
        +
        +	/**
        +	 * Get the min reactive power out of allowedCharge, allowedApparent and writeMin of setReactivePower channels
        +	 *
        +	 * @return min allowed reactivePower
        +	 */
        +	public long getMinReactivePower() {
        +		long minPower = 0;
        +		boolean valid = false;
        +		if (allowedApparent.valueOptional().isPresent()) {
        +			minPower = allowedApparent.valueOptional().get() * -1;
        +			valid = true;
        +		}
        +		if (valid && setReactivePower.writeMin().isPresent()) {
        +			minPower = Math.max(minPower, setReactivePower.writeMin().get());
        +		} else if (setReactivePower.writeMin().isPresent()) {
        +			minPower = setReactivePower.writeMin().get();
        +		}
        +		if (!valid) {
        +			log.debug("Failed to get Min value for ReactivePower! Return 0.");
        +		}
        +		return minPower;
        +	}
        +
        +}
        diff --git a/edge/src/io/openems/core/utilities/api/WriteJsonObject.java b/edge/src/io/openems/core/utilities/api/WriteJsonObject.java
        index 7f8ead00ff4..d29c0bbdec5 100644
        --- a/edge/src/io/openems/core/utilities/api/WriteJsonObject.java
        +++ b/edge/src/io/openems/core/utilities/api/WriteJsonObject.java
        @@ -3,7 +3,7 @@
         import com.google.gson.JsonElement;
         
         import io.openems.api.channel.WriteChannel;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         
         public class WriteJsonObject extends WriteObject {
         
        diff --git a/edge/src/io/openems/impl/controller/api/modbustcp/ChannelRegisterMap.java b/edge/src/io/openems/impl/controller/api/modbustcp/ChannelRegisterMap.java
        index 5099a5a1976..81f1d6a7744 100644
        --- a/edge/src/io/openems/impl/controller/api/modbustcp/ChannelRegisterMap.java
        +++ b/edge/src/io/openems/impl/controller/api/modbustcp/ChannelRegisterMap.java
        @@ -9,7 +9,7 @@
         import io.openems.api.channel.ConfigChannel;
         import io.openems.api.channel.WriteChannel;
         import io.openems.api.doc.ChannelDoc;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.types.ChannelAddress;
         import io.openems.core.ThingRepository;
         import io.openems.core.utilities.BitUtils;
        diff --git a/edge/src/io/openems/impl/controller/api/modbustcp/ModbusTcpApiController.java b/edge/src/io/openems/impl/controller/api/modbustcp/ModbusTcpApiController.java
        index ea95d47f140..a0c7a9f8c46 100644
        --- a/edge/src/io/openems/impl/controller/api/modbustcp/ModbusTcpApiController.java
        +++ b/edge/src/io/openems/impl/controller/api/modbustcp/ModbusTcpApiController.java
        @@ -16,10 +16,10 @@
         import io.openems.api.doc.ChannelDoc;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.types.ChannelAddress;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.ThingRepository;
        -import io.openems.core.utilities.JsonUtils;
         import io.openems.core.utilities.api.ApiWorker;
         
         @ThingInfo(title = "Modbus/TCP API", description = "Modbus/TCP slave implementation.")
        diff --git a/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java b/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java
        index c6cd7fe7cc7..46dfa6a8015 100644
        --- a/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java
        +++ b/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java
        @@ -17,7 +17,7 @@
         import com.ghgande.j2mod.modbus.procimg.Register;
         
         import io.openems.api.doc.ChannelDoc;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.types.ChannelAddress;
         import io.openems.core.utilities.api.ApiWorker;
         
        diff --git a/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java b/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java
        index cf677fda44c..ad52b867d9c 100644
        --- a/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java
        +++ b/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java
        @@ -8,8 +8,8 @@
         import com.ghgande.j2mod.modbus.procimg.Register;
         
         import io.openems.api.channel.Channel;
        -import io.openems.api.exception.NotImplementedException;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.NotImplementedException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.core.Databus;
         import io.openems.core.utilities.BitUtils;
         
        diff --git a/edge/src/io/openems/impl/controller/api/rest/ComponentSingleton.java b/edge/src/io/openems/impl/controller/api/rest/ComponentSingleton.java
        index f39a440ce0f..eab553f8278 100644
        --- a/edge/src/io/openems/impl/controller/api/rest/ComponentSingleton.java
        +++ b/edge/src/io/openems/impl/controller/api/rest/ComponentSingleton.java
        @@ -1,89 +1,89 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.rest;
        -
        -import org.restlet.Component;
        -import org.restlet.data.Protocol;
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import io.openems.api.channel.ConfigChannel;
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.core.utilities.api.ApiWorker;
        -
        -public class ComponentSingleton {
        -
        -	private static Component instance = null;
        -	private static Integer port = null;
        -
        -	private final static Logger log = LoggerFactory.getLogger(ComponentSingleton.class);
        -
        -	protected static synchronized Component getComponent(ConfigChannel port, ApiWorker apiWorker) throws OpenemsException {
        -		if (port.valueOptional().isPresent()) {
        -			return getComponent(port.valueOptional().get(), apiWorker);
        -		}
        -		throw new OpenemsException("Unable to start REST-Api: port is not set");
        -	}
        -
        -	protected static synchronized Component getComponent(int port, ApiWorker apiWorker) throws OpenemsException {
        -		if (ComponentSingleton.instance != null
        -				&& (ComponentSingleton.port == null || ComponentSingleton.port != port)) {
        -			// port changed -> restart
        -			ComponentSingleton.restartComponent(port, apiWorker);
        -		}
        -		if (ComponentSingleton.instance == null) {
        -			// instance not available -> start
        -			startComponent(port, apiWorker);
        -		}
        -		return ComponentSingleton.instance;
        -	}
        -
        -	protected static synchronized void restartComponent(int port, ApiWorker apiWorker) throws OpenemsException {
        -		stopComponent();
        -		startComponent(port, apiWorker);
        -	}
        -
        -	private static synchronized void startComponent(int port, ApiWorker apiWorker) throws OpenemsException {
        -		ComponentSingleton.instance = new Component();
        -		ComponentSingleton.instance.getServers().add(Protocol.HTTP, port);
        -		ComponentSingleton.instance.getDefaultHost().attach("/rest", new RestApiApplication(apiWorker));
        -		try {
        -			ComponentSingleton.instance.start();
        -			ComponentSingleton.port = port;
        -			log.info("REST-Api started on port [" + port + "].");
        -		} catch (Exception e) {
        -			throw new OpenemsException("REST-Api failed on port [" + port + "].", e);
        -		}
        -	}
        -
        -	protected static void stopComponent() {
        -		if (ComponentSingleton.instance != null) {
        -			try {
        -				ComponentSingleton.instance.stop();
        -				log.error("REST-Api stopped.");
        -			} catch (Exception e) {
        -				log.error("REST-Api failed to stop.", e);
        -			}
        -			ComponentSingleton.instance = null;
        -			ComponentSingleton.port = null;
        -		}
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.rest;
        +
        +import org.restlet.Component;
        +import org.restlet.data.Protocol;
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import io.openems.api.channel.ConfigChannel;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.core.utilities.api.ApiWorker;
        +
        +public class ComponentSingleton {
        +
        +	private static Component instance = null;
        +	private static Integer port = null;
        +
        +	private final static Logger log = LoggerFactory.getLogger(ComponentSingleton.class);
        +
        +	protected static synchronized Component getComponent(ConfigChannel port, ApiWorker apiWorker) throws OpenemsException {
        +		if (port.valueOptional().isPresent()) {
        +			return getComponent(port.valueOptional().get(), apiWorker);
        +		}
        +		throw new OpenemsException("Unable to start REST-Api: port is not set");
        +	}
        +
        +	protected static synchronized Component getComponent(int port, ApiWorker apiWorker) throws OpenemsException {
        +		if (ComponentSingleton.instance != null
        +				&& (ComponentSingleton.port == null || ComponentSingleton.port != port)) {
        +			// port changed -> restart
        +			ComponentSingleton.restartComponent(port, apiWorker);
        +		}
        +		if (ComponentSingleton.instance == null) {
        +			// instance not available -> start
        +			startComponent(port, apiWorker);
        +		}
        +		return ComponentSingleton.instance;
        +	}
        +
        +	protected static synchronized void restartComponent(int port, ApiWorker apiWorker) throws OpenemsException {
        +		stopComponent();
        +		startComponent(port, apiWorker);
        +	}
        +
        +	private static synchronized void startComponent(int port, ApiWorker apiWorker) throws OpenemsException {
        +		ComponentSingleton.instance = new Component();
        +		ComponentSingleton.instance.getServers().add(Protocol.HTTP, port);
        +		ComponentSingleton.instance.getDefaultHost().attach("/rest", new RestApiApplication(apiWorker));
        +		try {
        +			ComponentSingleton.instance.start();
        +			ComponentSingleton.port = port;
        +			log.info("REST-Api started on port [" + port + "].");
        +		} catch (Exception e) {
        +			throw new OpenemsException("REST-Api failed on port [" + port + "].", e);
        +		}
        +	}
        +
        +	protected static void stopComponent() {
        +		if (ComponentSingleton.instance != null) {
        +			try {
        +				ComponentSingleton.instance.stop();
        +				log.error("REST-Api stopped.");
        +			} catch (Exception e) {
        +				log.error("REST-Api failed to stop.", e);
        +			}
        +			ComponentSingleton.instance = null;
        +			ComponentSingleton.port = null;
        +		}
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/api/rest/RestApiController.java b/edge/src/io/openems/impl/controller/api/rest/RestApiController.java
        index 8b7dfbc64b9..f952fba8425 100644
        --- a/edge/src/io/openems/impl/controller/api/rest/RestApiController.java
        +++ b/edge/src/io/openems/impl/controller/api/rest/RestApiController.java
        @@ -28,7 +28,7 @@
         import io.openems.api.controller.Controller;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.core.utilities.api.ApiWorker;
         
         @ThingInfo(title = "REST-Api", description = "Use for external access to OpenEMS.")
        diff --git a/edge/src/io/openems/impl/controller/api/rest/internal/OpenemsEnroler.java b/edge/src/io/openems/impl/controller/api/rest/internal/OpenemsEnroler.java
        index 7feb0ad77db..41aa3db0472 100644
        --- a/edge/src/io/openems/impl/controller/api/rest/internal/OpenemsEnroler.java
        +++ b/edge/src/io/openems/impl/controller/api/rest/internal/OpenemsEnroler.java
        @@ -1,44 +1,44 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.rest.internal;
        -
        -import org.restlet.Application;
        -import org.restlet.data.ClientInfo;
        -import org.restlet.security.Enroler;
        -import org.restlet.security.Role;
        -
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.api.security.User;
        -
        -public class OpenemsEnroler implements Enroler {
        -
        -	@Override
        -	public void enrole(ClientInfo clientInfo) {
        -		String username = clientInfo.getUser().getIdentifier();
        -		User user;
        -		try {
        -			user = User.getUserByName(username);
        -			clientInfo.getRoles().add( //
        -					Role.get(Application.getCurrent(), user.getRole().name().toLowerCase()) //
        -			);
        -		} catch (OpenemsException e) { /* ignore, just don't enrole user in any group */ }
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.rest.internal;
        +
        +import org.restlet.Application;
        +import org.restlet.data.ClientInfo;
        +import org.restlet.security.Enroler;
        +import org.restlet.security.Role;
        +
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.api.security.User;
        +
        +public class OpenemsEnroler implements Enroler {
        +
        +	@Override
        +	public void enrole(ClientInfo clientInfo) {
        +		String username = clientInfo.getUser().getIdentifier();
        +		User user;
        +		try {
        +			user = User.getUserByName(username);
        +			clientInfo.getRoles().add( //
        +					Role.get(Application.getCurrent(), user.getRole().name().toLowerCase()) //
        +			);
        +		} catch (OpenemsException e) { /* ignore, just don't enrole user in any group */ }
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/api/rest/route/ChannelRestlet.java b/edge/src/io/openems/impl/controller/api/rest/route/ChannelRestlet.java
        index 74ea11a6bee..fd710cf33e1 100644
        --- a/edge/src/io/openems/impl/controller/api/rest/route/ChannelRestlet.java
        +++ b/edge/src/io/openems/impl/controller/api/rest/route/ChannelRestlet.java
        @@ -42,7 +42,7 @@
         import io.openems.api.channel.Channel;
         import io.openems.api.channel.ConfigChannel;
         import io.openems.api.channel.WriteChannel;
        -import io.openems.api.exception.NotImplementedException;
        +import io.openems.common.exceptions.NotImplementedException;
         import io.openems.common.session.Role;
         import io.openems.common.websocket.Notification;
         import io.openems.core.ThingRepository;
        diff --git a/edge/src/io/openems/impl/controller/api/rest/route/UserChangePasswordRestlet.java b/edge/src/io/openems/impl/controller/api/rest/route/UserChangePasswordRestlet.java
        index 29d3e45c617..be4dfec848a 100644
        --- a/edge/src/io/openems/impl/controller/api/rest/route/UserChangePasswordRestlet.java
        +++ b/edge/src/io/openems/impl/controller/api/rest/route/UserChangePasswordRestlet.java
        @@ -1,100 +1,99 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.api.rest.route;
        -
        -import org.restlet.Request;
        -import org.restlet.Response;
        -import org.restlet.data.Method;
        -import org.restlet.data.Status;
        -import org.restlet.resource.ResourceException;
        -import org.slf4j.Logger;
        -import org.slf4j.LoggerFactory;
        -
        -import com.google.gson.JsonObject;
        -import com.google.gson.JsonParser;
        -
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.api.exception.ReflectionException;
        -import io.openems.api.security.User;
        -import io.openems.core.utilities.JsonUtils;
        -import io.openems.impl.controller.api.rest.OpenemsRestlet;
        -
        -public class UserChangePasswordRestlet extends OpenemsRestlet {
        -
        -	private final Logger log = LoggerFactory.getLogger(UserChangePasswordRestlet.class);
        -
        -	public UserChangePasswordRestlet() {
        -		super();
        -	}
        -
        -	@Override
        -	public void handle(Request request, Response response) {
        -		super.handle(request, response);
        -
        -		// get user
        -		User user;
        -		try {
        -			user = User.getUserByName(request.getClientInfo().getUser().getIdentifier());
        -		} catch (OpenemsException e) {
        -			// User not found
        -			throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND);
        -		}
        -
        -		// check permission
        -		if (!isAuthenticatedAsRole(request, user.getRole())) {
        -			throw new ResourceException(Status.CLIENT_ERROR_UNAUTHORIZED);
        -		}
        -
        -		// call handler methods
        -		if (request.getMethod().equals(Method.POST)) {
        -			JsonParser parser = new JsonParser();
        -			String httpPost = request.getEntityAsText();
        -			JsonObject jHttpPost = parser.parse(httpPost).getAsJsonObject();
        -			changePassword(user, jHttpPost);
        -		}
        -	}
        -
        -	/**
        -	 * handle HTTP POST request
        -	 *
        -	 * @param thingId
        -	 * @param channelId
        -	 * @param jHttpPost
        -	 */
        -	private void changePassword(User user, JsonObject jHttpPost) {
        -		// parse old and new password
        -		String oldPassword;
        -		String newPassword;
        -		try {
        -			oldPassword = JsonUtils.getAsString(jHttpPost, "oldPassword");
        -			newPassword = JsonUtils.getAsString(jHttpPost, "newPassword");
        -		} catch (ReflectionException e1) {
        -			throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Value is missing");
        -		}
        -
        -		try {
        -			user.changePassword(oldPassword, newPassword);
        -			log.info("Changed password for user [" + user.getName() + "].");
        -		} catch (OpenemsException e) {
        -			throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Changing password failed: " + e.getMessage());
        -		}
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.api.rest.route;
        +
        +import org.restlet.Request;
        +import org.restlet.Response;
        +import org.restlet.data.Method;
        +import org.restlet.data.Status;
        +import org.restlet.resource.ResourceException;
        +import org.slf4j.Logger;
        +import org.slf4j.LoggerFactory;
        +
        +import com.google.gson.JsonObject;
        +import com.google.gson.JsonParser;
        +
        +import io.openems.api.security.User;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.utils.JsonUtils;
        +import io.openems.impl.controller.api.rest.OpenemsRestlet;
        +
        +public class UserChangePasswordRestlet extends OpenemsRestlet {
        +
        +	private final Logger log = LoggerFactory.getLogger(UserChangePasswordRestlet.class);
        +
        +	public UserChangePasswordRestlet() {
        +		super();
        +	}
        +
        +	@Override
        +	public void handle(Request request, Response response) {
        +		super.handle(request, response);
        +
        +		// get user
        +		User user;
        +		try {
        +			user = User.getUserByName(request.getClientInfo().getUser().getIdentifier());
        +		} catch (OpenemsException e) {
        +			// User not found
        +			throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND);
        +		}
        +
        +		// check permission
        +		if (!isAuthenticatedAsRole(request, user.getRole())) {
        +			throw new ResourceException(Status.CLIENT_ERROR_UNAUTHORIZED);
        +		}
        +
        +		// call handler methods
        +		if (request.getMethod().equals(Method.POST)) {
        +			JsonParser parser = new JsonParser();
        +			String httpPost = request.getEntityAsText();
        +			JsonObject jHttpPost = parser.parse(httpPost).getAsJsonObject();
        +			changePassword(user, jHttpPost);
        +		}
        +	}
        +
        +	/**
        +	 * handle HTTP POST request
        +	 *
        +	 * @param thingId
        +	 * @param channelId
        +	 * @param jHttpPost
        +	 */
        +	private void changePassword(User user, JsonObject jHttpPost) {
        +		// parse old and new password
        +		String oldPassword;
        +		String newPassword;
        +		try {
        +			oldPassword = JsonUtils.getAsString(jHttpPost, "oldPassword");
        +			newPassword = JsonUtils.getAsString(jHttpPost, "newPassword");
        +		} catch (OpenemsException e1) {
        +			throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Value is missing");
        +		}
        +
        +		try {
        +			user.changePassword(oldPassword, newPassword);
        +			log.info("Changed password for user [" + user.getName() + "].");
        +		} catch (OpenemsException e) {
        +			throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Changing password failed: " + e.getMessage());
        +		}
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java
        index 7b0fb84cda2..336d21a46e8 100644
        --- a/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java
        +++ b/edge/src/io/openems/impl/controller/asymmetric/avoidtotaldischarge/Ess.java
        @@ -1,67 +1,67 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.asymmetric.avoidtotaldischarge;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.AsymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -
        -@IsThingMap(type = AsymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -	public ReadChannel soc;
        -	public ReadChannel minSoc;
        -	public ReadChannel chargeSoc;
        -	public WriteChannel setActivePowerL1;
        -	public WriteChannel setActivePowerL2;
        -	public WriteChannel setActivePowerL3;
        -	public WriteChannel setReactivePowerL1;
        -	public WriteChannel setReactivePowerL2;
        -	public WriteChannel setReactivePowerL3;
        -	public ReadChannel systemState;
        -	public ReadChannel allowedCharge;
        -	public State currentState = State.NORMAL;
        -
        -	public enum State {
        -		NORMAL, MINSOC, CHARGESOC, FULL;
        -	}
        -
        -	public Ess(AsymmetricEssNature ess) {
        -		super(ess);
        -		minSoc = ess.minSoc().required();
        -		chargeSoc = ess.chargeSoc().required();
        -		setActivePowerL1 = ess.setActivePowerL1().required();
        -		setActivePowerL2 = ess.setActivePowerL2().required();
        -		setActivePowerL3 = ess.setActivePowerL3().required();
        -		setReactivePowerL1 = ess.setReactivePowerL1().required();
        -		setReactivePowerL2 = ess.setReactivePowerL2().required();
        -		setReactivePowerL3 = ess.setReactivePowerL3().required();
        -		soc = ess.soc().required();
        -		systemState = ess.systemState().required();
        -		allowedCharge = ess.allowedCharge().required();
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.asymmetric.avoidtotaldischarge;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.AsymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +
        +@IsThingMap(type = AsymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +	public ReadChannel soc;
        +	public ReadChannel minSoc;
        +	public ReadChannel chargeSoc;
        +	public WriteChannel setActivePowerL1;
        +	public WriteChannel setActivePowerL2;
        +	public WriteChannel setActivePowerL3;
        +	public WriteChannel setReactivePowerL1;
        +	public WriteChannel setReactivePowerL2;
        +	public WriteChannel setReactivePowerL3;
        +	public ReadChannel systemState;
        +	public ReadChannel allowedCharge;
        +	public State currentState = State.NORMAL;
        +
        +	public enum State {
        +		NORMAL, MINSOC, CHARGESOC, FULL;
        +	}
        +
        +	public Ess(AsymmetricEssNature ess) {
        +		super(ess);
        +		minSoc = ess.minSoc().required();
        +		chargeSoc = ess.chargeSoc().required();
        +		setActivePowerL1 = ess.setActivePowerL1().required();
        +		setActivePowerL2 = ess.setActivePowerL2().required();
        +		setActivePowerL3 = ess.setActivePowerL3().required();
        +		setReactivePowerL1 = ess.setReactivePowerL1().required();
        +		setReactivePowerL2 = ess.setReactivePowerL2().required();
        +		setReactivePowerL3 = ess.setReactivePowerL3().required();
        +		soc = ess.soc().required();
        +		systemState = ess.systemState().required();
        +		allowedCharge = ess.allowedCharge().required();
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/asymmetric/balancing/Ess.java b/edge/src/io/openems/impl/controller/asymmetric/balancing/Ess.java
        index 21cb64a3ffb..735a3219884 100644
        --- a/edge/src/io/openems/impl/controller/asymmetric/balancing/Ess.java
        +++ b/edge/src/io/openems/impl/controller/asymmetric/balancing/Ess.java
        @@ -1,126 +1,126 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.asymmetric.balancing;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.AsymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -
        -@IsThingMap(type = AsymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -	public ReadChannel soc;
        -	public ReadChannel activePowerL1;
        -	public ReadChannel activePowerL2;
        -	public ReadChannel activePowerL3;
        -	public ReadChannel allowedCharge;
        -	public ReadChannel allowedDischarge;
        -	public ReadChannel minSoc;
        -	public WriteChannel setWorkState;
        -	public WriteChannel setActivePowerL1;
        -	public WriteChannel setActivePowerL2;
        -	public WriteChannel setActivePowerL3;
        -	public WriteChannel setReactivePowerL1;
        -	public WriteChannel setReactivePowerL2;
        -	public WriteChannel setReactivePowerL3;
        -	public ReadChannel allowedApparent;
        -
        -	public Ess(AsymmetricEssNature ess) {
        -		super(ess);
        -		activePowerL1 = ess.activePowerL1().required();
        -		activePowerL2 = ess.activePowerL2().required();
        -		activePowerL3 = ess.activePowerL3().required();
        -		allowedCharge = ess.allowedCharge().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		minSoc = ess.minSoc().required();
        -		setActivePowerL1 = ess.setActivePowerL1().required();
        -		setActivePowerL2 = ess.setActivePowerL2().required();
        -		setActivePowerL3 = ess.setActivePowerL3().required();
        -		setReactivePowerL1 = ess.setReactivePowerL1().required();
        -		setReactivePowerL2 = ess.setReactivePowerL2().required();
        -		setReactivePowerL3 = ess.setReactivePowerL3().required();
        -		soc = ess.soc().required();
        -		setWorkState = ess.setWorkState().required();
        -		allowedApparent = ess.allowedApparent().required();
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -
        -	public ReadChannel getActivePower(int phase) {
        -		ReadChannel channel = null;
        -		switch (phase) {
        -		case 1:
        -			channel = activePowerL1;
        -			break;
        -		case 2:
        -			channel = activePowerL2;
        -			break;
        -		case 3:
        -			channel = activePowerL3;
        -			break;
        -		}
        -		return channel;
        -	}
        -
        -	public WriteChannel getSetActivePower(int phase) {
        -		WriteChannel channel = null;
        -		switch (phase) {
        -		case 1:
        -			channel = setActivePowerL1;
        -			break;
        -		case 2:
        -			channel = setActivePowerL2;
        -			break;
        -		case 3:
        -			channel = setActivePowerL3;
        -			break;
        -		}
        -		return channel;
        -	}
        -
        -	public WriteChannel getSetReactivePower(int phase) {
        -		WriteChannel channel = null;
        -		switch (phase) {
        -		case 1:
        -			channel = setReactivePowerL1;
        -			break;
        -		case 2:
        -			channel = setReactivePowerL2;
        -			break;
        -		case 3:
        -			channel = setReactivePowerL3;
        -			break;
        -		}
        -		return channel;
        -	}
        -
        -	public String getSetValueLog() {
        -		return String.format(
        -				"%s : Set ActivePower L1: %d W L2: %d W L3: %d W , Set ReactivePower L1: %d Var L2: %d Var L3: %d Var",
        -				id(), setActivePowerL1.peekWrite().orElse(null), setActivePowerL2.peekWrite().orElse(null),
        -				setActivePowerL3.peekWrite().orElse(null), setReactivePowerL1.peekWrite().orElse(null),
        -				setReactivePowerL2.peekWrite().orElse(null), setReactivePowerL3.peekWrite().orElse(null));
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.asymmetric.balancing;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.AsymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +
        +@IsThingMap(type = AsymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +	public ReadChannel soc;
        +	public ReadChannel activePowerL1;
        +	public ReadChannel activePowerL2;
        +	public ReadChannel activePowerL3;
        +	public ReadChannel allowedCharge;
        +	public ReadChannel allowedDischarge;
        +	public ReadChannel minSoc;
        +	public WriteChannel setWorkState;
        +	public WriteChannel setActivePowerL1;
        +	public WriteChannel setActivePowerL2;
        +	public WriteChannel setActivePowerL3;
        +	public WriteChannel setReactivePowerL1;
        +	public WriteChannel setReactivePowerL2;
        +	public WriteChannel setReactivePowerL3;
        +	public ReadChannel allowedApparent;
        +
        +	public Ess(AsymmetricEssNature ess) {
        +		super(ess);
        +		activePowerL1 = ess.activePowerL1().required();
        +		activePowerL2 = ess.activePowerL2().required();
        +		activePowerL3 = ess.activePowerL3().required();
        +		allowedCharge = ess.allowedCharge().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		minSoc = ess.minSoc().required();
        +		setActivePowerL1 = ess.setActivePowerL1().required();
        +		setActivePowerL2 = ess.setActivePowerL2().required();
        +		setActivePowerL3 = ess.setActivePowerL3().required();
        +		setReactivePowerL1 = ess.setReactivePowerL1().required();
        +		setReactivePowerL2 = ess.setReactivePowerL2().required();
        +		setReactivePowerL3 = ess.setReactivePowerL3().required();
        +		soc = ess.soc().required();
        +		setWorkState = ess.setWorkState().required();
        +		allowedApparent = ess.allowedApparent().required();
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +
        +	public ReadChannel getActivePower(int phase) {
        +		ReadChannel channel = null;
        +		switch (phase) {
        +		case 1:
        +			channel = activePowerL1;
        +			break;
        +		case 2:
        +			channel = activePowerL2;
        +			break;
        +		case 3:
        +			channel = activePowerL3;
        +			break;
        +		}
        +		return channel;
        +	}
        +
        +	public WriteChannel getSetActivePower(int phase) {
        +		WriteChannel channel = null;
        +		switch (phase) {
        +		case 1:
        +			channel = setActivePowerL1;
        +			break;
        +		case 2:
        +			channel = setActivePowerL2;
        +			break;
        +		case 3:
        +			channel = setActivePowerL3;
        +			break;
        +		}
        +		return channel;
        +	}
        +
        +	public WriteChannel getSetReactivePower(int phase) {
        +		WriteChannel channel = null;
        +		switch (phase) {
        +		case 1:
        +			channel = setReactivePowerL1;
        +			break;
        +		case 2:
        +			channel = setReactivePowerL2;
        +			break;
        +		case 3:
        +			channel = setReactivePowerL3;
        +			break;
        +		}
        +		return channel;
        +	}
        +
        +	public String getSetValueLog() {
        +		return String.format(
        +				"%s : Set ActivePower L1: %d W L2: %d W L3: %d W , Set ReactivePower L1: %d Var L2: %d Var L3: %d Var",
        +				id(), setActivePowerL1.peekWrite().orElse(null), setActivePowerL2.peekWrite().orElse(null),
        +				setActivePowerL3.peekWrite().orElse(null), setReactivePowerL1.peekWrite().orElse(null),
        +				setReactivePowerL2.peekWrite().orElse(null), setReactivePowerL3.peekWrite().orElse(null));
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/asymmetric/balancingBandgap/Ess.java b/edge/src/io/openems/impl/controller/asymmetric/balancingBandgap/Ess.java
        index 923214d574c..f48944537f4 100644
        --- a/edge/src/io/openems/impl/controller/asymmetric/balancingBandgap/Ess.java
        +++ b/edge/src/io/openems/impl/controller/asymmetric/balancingBandgap/Ess.java
        @@ -1,91 +1,91 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.asymmetric.balancingBandgap;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.AsymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.AsymmetricPower;
        -
        -@IsThingMap(type = AsymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -	public ReadChannel soc;
        -	public ReadChannel activePowerL1;
        -	public ReadChannel activePowerL2;
        -	public ReadChannel activePowerL3;
        -	public ReadChannel reactivePowerL1;
        -	public ReadChannel reactivePowerL2;
        -	public ReadChannel reactivePowerL3;
        -	public ReadChannel allowedCharge;
        -	public ReadChannel allowedDischarge;
        -	public ReadChannel minSoc;
        -	public WriteChannel setWorkState;
        -	public WriteChannel setActivePowerL1;
        -	public WriteChannel setActivePowerL2;
        -	public WriteChannel setActivePowerL3;
        -	public WriteChannel setReactivePowerL1;
        -	public WriteChannel setReactivePowerL2;
        -	public WriteChannel setReactivePowerL3;
        -	public ReadChannel allowedApparent;
        -	public AsymmetricPower power;
        -
        -	public Ess(AsymmetricEssNature ess) {
        -		super(ess);
        -		activePowerL1 = ess.activePowerL1().required();
        -		activePowerL2 = ess.activePowerL2().required();
        -		activePowerL3 = ess.activePowerL3().required();
        -		reactivePowerL1 = ess.reactivePowerL1().required();
        -		reactivePowerL2 = ess.reactivePowerL2().required();
        -		reactivePowerL3 = ess.reactivePowerL3().required();
        -		allowedCharge = ess.allowedCharge().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		minSoc = ess.minSoc().required();
        -		setActivePowerL1 = ess.setActivePowerL1().required();
        -		setActivePowerL2 = ess.setActivePowerL2().required();
        -		setActivePowerL3 = ess.setActivePowerL3().required();
        -		setReactivePowerL1 = ess.setReactivePowerL1().required();
        -		setReactivePowerL2 = ess.setReactivePowerL2().required();
        -		setReactivePowerL3 = ess.setReactivePowerL3().required();
        -		soc = ess.soc().required();
        -		setWorkState = ess.setWorkState().required();
        -		allowedApparent = ess.allowedApparent().required();
        -		power = new AsymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        -				ess.allowedApparent().required(), ess.setActivePowerL1().required(), ess.setActivePowerL2().required(),
        -				ess.setActivePowerL3().required(), ess.setReactivePowerL1().required(),
        -				ess.setReactivePowerL2().required(), ess.setReactivePowerL3().required());
        -
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -
        -	public String getSetValueLog() {
        -		return String.format(
        -				"%s : Set ActivePower L1: %d W L2: %d W L3: %d W , Set ReactivePower L1: %d Var L2: %d Var L3: %d Var",
        -				id(), setActivePowerL1.peekWrite().orElse(null), setActivePowerL2.peekWrite().orElse(null),
        -				setActivePowerL3.peekWrite().orElse(null), setReactivePowerL1.peekWrite().orElse(null),
        -				setReactivePowerL2.peekWrite().orElse(null), setReactivePowerL3.peekWrite().orElse(null));
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.asymmetric.balancingBandgap;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.AsymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.core.utilities.AsymmetricPower;
        +
        +@IsThingMap(type = AsymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +	public ReadChannel soc;
        +	public ReadChannel activePowerL1;
        +	public ReadChannel activePowerL2;
        +	public ReadChannel activePowerL3;
        +	public ReadChannel reactivePowerL1;
        +	public ReadChannel reactivePowerL2;
        +	public ReadChannel reactivePowerL3;
        +	public ReadChannel allowedCharge;
        +	public ReadChannel allowedDischarge;
        +	public ReadChannel minSoc;
        +	public WriteChannel setWorkState;
        +	public WriteChannel setActivePowerL1;
        +	public WriteChannel setActivePowerL2;
        +	public WriteChannel setActivePowerL3;
        +	public WriteChannel setReactivePowerL1;
        +	public WriteChannel setReactivePowerL2;
        +	public WriteChannel setReactivePowerL3;
        +	public ReadChannel allowedApparent;
        +	public AsymmetricPower power;
        +
        +	public Ess(AsymmetricEssNature ess) {
        +		super(ess);
        +		activePowerL1 = ess.activePowerL1().required();
        +		activePowerL2 = ess.activePowerL2().required();
        +		activePowerL3 = ess.activePowerL3().required();
        +		reactivePowerL1 = ess.reactivePowerL1().required();
        +		reactivePowerL2 = ess.reactivePowerL2().required();
        +		reactivePowerL3 = ess.reactivePowerL3().required();
        +		allowedCharge = ess.allowedCharge().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		minSoc = ess.minSoc().required();
        +		setActivePowerL1 = ess.setActivePowerL1().required();
        +		setActivePowerL2 = ess.setActivePowerL2().required();
        +		setActivePowerL3 = ess.setActivePowerL3().required();
        +		setReactivePowerL1 = ess.setReactivePowerL1().required();
        +		setReactivePowerL2 = ess.setReactivePowerL2().required();
        +		setReactivePowerL3 = ess.setReactivePowerL3().required();
        +		soc = ess.soc().required();
        +		setWorkState = ess.setWorkState().required();
        +		allowedApparent = ess.allowedApparent().required();
        +		power = new AsymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        +				ess.allowedApparent().required(), ess.setActivePowerL1().required(), ess.setActivePowerL2().required(),
        +				ess.setActivePowerL3().required(), ess.setReactivePowerL1().required(),
        +				ess.setReactivePowerL2().required(), ess.setReactivePowerL3().required());
        +
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +
        +	public String getSetValueLog() {
        +		return String.format(
        +				"%s : Set ActivePower L1: %d W L2: %d W L3: %d W , Set ReactivePower L1: %d Var L2: %d Var L3: %d Var",
        +				id(), setActivePowerL1.peekWrite().orElse(null), setActivePowerL2.peekWrite().orElse(null),
        +				setActivePowerL3.peekWrite().orElse(null), setReactivePowerL1.peekWrite().orElse(null),
        +				setReactivePowerL2.peekWrite().orElse(null), setReactivePowerL3.peekWrite().orElse(null));
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/asymmetric/balancingcurrent/Meter.java b/edge/src/io/openems/impl/controller/asymmetric/balancingcurrent/Meter.java
        index fc6f47fef33..9662ecee4d4 100644
        --- a/edge/src/io/openems/impl/controller/asymmetric/balancingcurrent/Meter.java
        +++ b/edge/src/io/openems/impl/controller/asymmetric/balancingcurrent/Meter.java
        @@ -1,69 +1,69 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.asymmetric.balancingcurrent;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.meter.AsymmetricMeterNature;
        -import io.openems.api.exception.InvalidValueException;
        -
        -@IsThingMap(type = AsymmetricMeterNature.class)
        -public class Meter extends ThingMap {
        -
        -	public final ReadChannel currentL1;
        -	public final ReadChannel currentL2;
        -	public final ReadChannel currentL3;
        -	public final ReadChannel voltageL1;
        -	public final ReadChannel voltageL2;
        -	public final ReadChannel voltageL3;
        -	public final ReadChannel activePowerL1;
        -	public final ReadChannel reactivePowerL1;
        -	public final ReadChannel activePowerL2;
        -	public final ReadChannel reactivePowerL2;
        -	public final ReadChannel activePowerL3;
        -	public final ReadChannel reactivePowerL3;
        -
        -	public Meter(AsymmetricMeterNature meter) {
        -		super(meter);
        -		currentL1 = meter.currentL1().required();
        -		currentL2 = meter.currentL2().required();
        -		currentL3 = meter.currentL3().required();
        -		voltageL1 = meter.voltageL1().required();
        -		voltageL2 = meter.voltageL2().required();
        -		voltageL3 = meter.voltageL3().required();
        -		activePowerL1 = meter.activePowerL1();
        -		activePowerL2 = meter.activePowerL2();
        -		activePowerL3 = meter.activePowerL3();
        -		reactivePowerL1 = meter.reactivePowerL1();
        -		reactivePowerL2 = meter.reactivePowerL2();
        -		reactivePowerL3 = meter.reactivePowerL3();
        -	}
        -
        -	public Long getActivePower() throws InvalidValueException {
        -		return activePowerL1.value() + activePowerL2.value() + activePowerL3.value();
        -	}
        -
        -	public Long getReactivePower() throws InvalidValueException {
        -		return reactivePowerL1.value() + reactivePowerL2.value() + reactivePowerL3.value();
        -	}
        -
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.asymmetric.balancingcurrent;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.meter.AsymmetricMeterNature;
        +import io.openems.api.exception.InvalidValueException;
        +
        +@IsThingMap(type = AsymmetricMeterNature.class)
        +public class Meter extends ThingMap {
        +
        +	public final ReadChannel currentL1;
        +	public final ReadChannel currentL2;
        +	public final ReadChannel currentL3;
        +	public final ReadChannel voltageL1;
        +	public final ReadChannel voltageL2;
        +	public final ReadChannel voltageL3;
        +	public final ReadChannel activePowerL1;
        +	public final ReadChannel reactivePowerL1;
        +	public final ReadChannel activePowerL2;
        +	public final ReadChannel reactivePowerL2;
        +	public final ReadChannel activePowerL3;
        +	public final ReadChannel reactivePowerL3;
        +
        +	public Meter(AsymmetricMeterNature meter) {
        +		super(meter);
        +		currentL1 = meter.currentL1().required();
        +		currentL2 = meter.currentL2().required();
        +		currentL3 = meter.currentL3().required();
        +		voltageL1 = meter.voltageL1().required();
        +		voltageL2 = meter.voltageL2().required();
        +		voltageL3 = meter.voltageL3().required();
        +		activePowerL1 = meter.activePowerL1();
        +		activePowerL2 = meter.activePowerL2();
        +		activePowerL3 = meter.activePowerL3();
        +		reactivePowerL1 = meter.reactivePowerL1();
        +		reactivePowerL2 = meter.reactivePowerL2();
        +		reactivePowerL3 = meter.reactivePowerL3();
        +	}
        +
        +	public Long getActivePower() throws InvalidValueException {
        +		return activePowerL1.value() + activePowerL2.value() + activePowerL3.value();
        +	}
        +
        +	public Long getReactivePower() throws InvalidValueException {
        +		return reactivePowerL1.value() + reactivePowerL2.value() + reactivePowerL3.value();
        +	}
        +
        +}
        diff --git a/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java b/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java
        index 14b31506e3c..5bbeea84162 100644
        --- a/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java
        +++ b/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java
        @@ -1,69 +1,69 @@
        -/*******************************************************************************
        - * 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.chargerlimitation;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.AsymmetricEssNature;
        -import io.openems.api.exception.WriteChannelException;
        -
        -@IsThingMap(type = AsymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public ReadChannel soc;
        -	public ReadChannel allowedCharge;
        -	public WriteChannel setActivePowerL1;
        -	public WriteChannel setActivePowerL2;
        -	public WriteChannel setActivePowerL3;
        -	public ReadChannel activePowerL1;
        -	public ReadChannel activePowerL2;
        -	public ReadChannel activePowerL3;
        -
        -	public Ess(AsymmetricEssNature thing) {
        -		super(thing);
        -		this.soc = thing.soc().required();
        -		this.allowedCharge = thing.allowedCharge().required();
        -		this.setActivePowerL1 = thing.setActivePowerL1();
        -		this.setActivePowerL2 = thing.setActivePowerL2();
        -		this.setActivePowerL3 = thing.setActivePowerL3();
        -		this.activePowerL1 = thing.activePowerL1();
        -		this.activePowerL2 = thing.activePowerL2();
        -		this.activePowerL3 = thing.activePowerL3();
        -	}
        -
        -	public Long getWrittenActivePower() {
        -		long writePower = setActivePowerL1.peekWrite().orElse(0l) + setActivePowerL2.peekWrite().orElse(0l)
        -				+ setActivePowerL3.peekWrite().orElse(0l);
        -		long currentPower = activePowerL1.valueOptional().orElse(0L) + activePowerL2.valueOptional().orElse(0L)
        -				+ activePowerL3.valueOptional().orElse(0L);
        -		return writePower < currentPower ? writePower : currentPower;
        -	}
        -
        -	public void setMaxCharge(float power) throws WriteChannelException {
        -		power *= -1;
        -		setActivePowerL1.pushWriteMin((long) (power / 3f));
        -		setActivePowerL2.pushWriteMin((long) (power / 3f));
        -		setActivePowerL3.pushWriteMin((long) (power / 3f));
        -	}
        -
        -}
        +/*******************************************************************************
        + * 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.chargerlimitation;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.AsymmetricEssNature;
        +import io.openems.api.exception.WriteChannelException;
        +
        +@IsThingMap(type = AsymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public ReadChannel soc;
        +	public ReadChannel allowedCharge;
        +	public WriteChannel setActivePowerL1;
        +	public WriteChannel setActivePowerL2;
        +	public WriteChannel setActivePowerL3;
        +	public ReadChannel activePowerL1;
        +	public ReadChannel activePowerL2;
        +	public ReadChannel activePowerL3;
        +
        +	public Ess(AsymmetricEssNature thing) {
        +		super(thing);
        +		this.soc = thing.soc().required();
        +		this.allowedCharge = thing.allowedCharge().required();
        +		this.setActivePowerL1 = thing.setActivePowerL1();
        +		this.setActivePowerL2 = thing.setActivePowerL2();
        +		this.setActivePowerL3 = thing.setActivePowerL3();
        +		this.activePowerL1 = thing.activePowerL1();
        +		this.activePowerL2 = thing.activePowerL2();
        +		this.activePowerL3 = thing.activePowerL3();
        +	}
        +
        +	public Long getWrittenActivePower() {
        +		long writePower = setActivePowerL1.peekWrite().orElse(0l) + setActivePowerL2.peekWrite().orElse(0l)
        +				+ setActivePowerL3.peekWrite().orElse(0l);
        +		long currentPower = activePowerL1.valueOptional().orElse(0L) + activePowerL2.valueOptional().orElse(0L)
        +				+ activePowerL3.valueOptional().orElse(0L);
        +		return writePower < currentPower ? writePower : currentPower;
        +	}
        +
        +	public void setMaxCharge(float power) throws WriteChannelException {
        +		power *= -1;
        +		setActivePowerL1.pushWriteMin((long) (power / 3f));
        +		setActivePowerL2.pushWriteMin((long) (power / 3f));
        +		setActivePowerL3.pushWriteMin((long) (power / 3f));
        +	}
        +
        +}
        diff --git a/edge/src/io/openems/impl/controller/supplybusswitch/SupplyBusSwitchController.java b/edge/src/io/openems/impl/controller/supplybusswitch/SupplyBusSwitchController.java
        index edad337ce3a..e49fa9c6bb0 100644
        --- a/edge/src/io/openems/impl/controller/supplybusswitch/SupplyBusSwitchController.java
        +++ b/edge/src/io/openems/impl/controller/supplybusswitch/SupplyBusSwitchController.java
        @@ -40,8 +40,10 @@
         import io.openems.api.exception.InvalidValueException;
         import io.openems.api.exception.ReflectionException;
         import io.openems.api.exception.WriteChannelException;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.ThingRepository;
        -import io.openems.core.utilities.JsonUtils;
        +
         
         // TODO better explanation
         @ThingInfo(title = "Supply Bus Switch")
        @@ -196,6 +198,8 @@ private List generateSupplybuses() {
         					log.error("can't find JsonElement 'bus' or 'switches' in config parameter 'supplyBuses'!", e);
         				} catch (InvalidValueException e3) {
         					log.error("primaryEss not found", e3);
        +				} catch (OpenemsException e4) {
        +					log.error(e4.getMessage());
         				}
         			}
         			return buses;
        diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java
        index e06ab42d434..22eba649e5f 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java
        @@ -1,101 +1,101 @@
        -/*******************************************************************************
        - * 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.symmetric.avoidtotaldischargesoctimeline;
        -
        -import java.time.LocalTime;
        -import java.util.Map;
        -import java.util.TreeMap;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -
        -@IsThingMap(type = SymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public final ReadChannel minSoc;
        -	public final WriteChannel setActivePower;
        -	public final ReadChannel soc;
        -	public final ReadChannel systemState;
        -	public int maxPowerPercent = 100;
        -	public final ReadChannel allowedDischarge;
        -	public final ReadChannel chargeSoc;
        -	private TreeMap timeline = new TreeMap<>();
        -	public State currentState = State.NORMAL;
        -
        -	public enum State {
        -		NORMAL, MINSOC, CHARGESOC
        -	}
        -
        -	public Ess(SymmetricEssNature ess) {
        -		super(ess);
        -		setActivePower = ess.setActivePower().required();
        -		systemState = ess.systemState().required();
        -		soc = ess.soc().required();
        -		minSoc = ess.minSoc().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		chargeSoc = ess.chargeSoc().required();
        -	}
        -
        -	public void addTime(LocalTime time, int minSoc, int chargeSoc) {
        -		Soc soc = new Soc(minSoc, chargeSoc);
        -		timeline.put(time, soc);
        -	}
        -
        -	public int getMinSoc(LocalTime time) throws InvalidValueException {
        -		Map.Entry entry = timeline.floorEntry(time);
        -		if (entry != null) {
        -			return entry.getValue().minSoc;
        -		}
        -		entry = timeline.lastEntry();
        -		if (entry != null) {
        -			return entry.getValue().minSoc;
        -		}
        -		return minSoc.value();
        -	}
        -
        -	public int getChargeSoc(LocalTime time) throws InvalidValueException {
        -		Map.Entry entry = timeline.floorEntry(time);
        -		if (entry != null) {
        -			return entry.getValue().chargeSoc;
        -		}
        -		entry = timeline.lastEntry();
        -		if (entry != null) {
        -			return entry.getValue().chargeSoc;
        -		}
        -		return chargeSoc.value();
        -	}
        -
        -	private class Soc {
        -		public final int minSoc;
        -		public final int chargeSoc;
        -
        -		public Soc(int minSoc, int chargeSoc) {
        -			super();
        -			this.minSoc = minSoc;
        -			this.chargeSoc = chargeSoc;
        -		}
        -
        -	}
        -}
        +/*******************************************************************************
        + * 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.symmetric.avoidtotaldischargesoctimeline;
        +
        +import java.time.LocalTime;
        +import java.util.Map;
        +import java.util.TreeMap;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.SymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +
        +@IsThingMap(type = SymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public final ReadChannel minSoc;
        +	public final WriteChannel setActivePower;
        +	public final ReadChannel soc;
        +	public final ReadChannel systemState;
        +	public int maxPowerPercent = 100;
        +	public final ReadChannel allowedDischarge;
        +	public final ReadChannel chargeSoc;
        +	private TreeMap timeline = new TreeMap<>();
        +	public State currentState = State.NORMAL;
        +
        +	public enum State {
        +		NORMAL, MINSOC, CHARGESOC
        +	}
        +
        +	public Ess(SymmetricEssNature ess) {
        +		super(ess);
        +		setActivePower = ess.setActivePower().required();
        +		systemState = ess.systemState().required();
        +		soc = ess.soc().required();
        +		minSoc = ess.minSoc().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		chargeSoc = ess.chargeSoc().required();
        +	}
        +
        +	public void addTime(LocalTime time, int minSoc, int chargeSoc) {
        +		Soc soc = new Soc(minSoc, chargeSoc);
        +		timeline.put(time, soc);
        +	}
        +
        +	public int getMinSoc(LocalTime time) throws InvalidValueException {
        +		Map.Entry entry = timeline.floorEntry(time);
        +		if (entry != null) {
        +			return entry.getValue().minSoc;
        +		}
        +		entry = timeline.lastEntry();
        +		if (entry != null) {
        +			return entry.getValue().minSoc;
        +		}
        +		return minSoc.value();
        +	}
        +
        +	public int getChargeSoc(LocalTime time) throws InvalidValueException {
        +		Map.Entry entry = timeline.floorEntry(time);
        +		if (entry != null) {
        +			return entry.getValue().chargeSoc;
        +		}
        +		entry = timeline.lastEntry();
        +		if (entry != null) {
        +			return entry.getValue().chargeSoc;
        +		}
        +		return chargeSoc.value();
        +	}
        +
        +	private class Soc {
        +		public final int minSoc;
        +		public final int chargeSoc;
        +
        +		public Soc(int minSoc, int chargeSoc) {
        +			super();
        +			this.minSoc = minSoc;
        +			this.chargeSoc = chargeSoc;
        +		}
        +
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java
        index 7595add10b2..0201cdd8508 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java
        @@ -1,65 +1,65 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.symmetric.balancingbandgap;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.SymmetricPower;
        -
        -@IsThingMap(type = SymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public final ReadChannel minSoc;
        -	public final WriteChannel setActivePower;
        -	public final WriteChannel setReactivePower;
        -	public final ReadChannel soc;
        -	public final ReadChannel activePower;
        -	public final ReadChannel reactivePower;
        -	public final ReadChannel allowedCharge;
        -	public final ReadChannel allowedDischarge;
        -	public final ReadChannel systemState;
        -	public final SymmetricPower power;
        -
        -	public Ess(SymmetricEssNature ess) {
        -		super(ess);
        -		minSoc = ess.minSoc().required();
        -
        -		setActivePower = ess.setActivePower().required();
        -		setReactivePower = ess.setReactivePower().required();
        -
        -		soc = ess.soc().required();
        -		activePower = ess.activePower().required();
        -		allowedCharge = ess.allowedCharge().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		systemState = ess.systemState().required();
        -		reactivePower = ess.reactivePower().required();
        -		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        -				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.symmetric.balancingbandgap;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.SymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.core.utilities.SymmetricPower;
        +
        +@IsThingMap(type = SymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public final ReadChannel minSoc;
        +	public final WriteChannel setActivePower;
        +	public final WriteChannel setReactivePower;
        +	public final ReadChannel soc;
        +	public final ReadChannel activePower;
        +	public final ReadChannel reactivePower;
        +	public final ReadChannel allowedCharge;
        +	public final ReadChannel allowedDischarge;
        +	public final ReadChannel systemState;
        +	public final SymmetricPower power;
        +
        +	public Ess(SymmetricEssNature ess) {
        +		super(ess);
        +		minSoc = ess.minSoc().required();
        +
        +		setActivePower = ess.setActivePower().required();
        +		setReactivePower = ess.setReactivePower().required();
        +
        +		soc = ess.soc().required();
        +		activePower = ess.activePower().required();
        +		allowedCharge = ess.allowedCharge().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		systemState = ess.systemState().required();
        +		reactivePower = ess.reactivePower().required();
        +		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        +				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Meter.java b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Meter.java
        index cbdc90826f2..f4c48e09a41 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Meter.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Meter.java
        @@ -1,69 +1,69 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.symmetric.balancingcurrent;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.meter.AsymmetricMeterNature;
        -import io.openems.api.exception.InvalidValueException;
        -
        -@IsThingMap(type = AsymmetricMeterNature.class)
        -public class Meter extends ThingMap {
        -
        -	public final ReadChannel currentL1;
        -	public final ReadChannel currentL2;
        -	public final ReadChannel currentL3;
        -	public final ReadChannel voltageL1;
        -	public final ReadChannel voltageL2;
        -	public final ReadChannel voltageL3;
        -	public final ReadChannel activePowerL1;
        -	public final ReadChannel reactivePowerL1;
        -	public final ReadChannel activePowerL2;
        -	public final ReadChannel reactivePowerL2;
        -	public final ReadChannel activePowerL3;
        -	public final ReadChannel reactivePowerL3;
        -
        -	public Meter(AsymmetricMeterNature meter) {
        -		super(meter);
        -		currentL1 = meter.currentL1().required();
        -		currentL2 = meter.currentL2().required();
        -		currentL3 = meter.currentL3().required();
        -		voltageL1 = meter.voltageL1().required();
        -		voltageL2 = meter.voltageL2().required();
        -		voltageL3 = meter.voltageL3().required();
        -		activePowerL1 = meter.activePowerL1();
        -		activePowerL2 = meter.activePowerL2();
        -		activePowerL3 = meter.activePowerL3();
        -		reactivePowerL1 = meter.reactivePowerL1();
        -		reactivePowerL2 = meter.reactivePowerL2();
        -		reactivePowerL3 = meter.reactivePowerL3();
        -	}
        -
        -	public Long getActivePower() throws InvalidValueException {
        -		return activePowerL1.value() + activePowerL2.value() + activePowerL3.value();
        -	}
        -
        -	public Long getReactivePower() throws InvalidValueException {
        -		return reactivePowerL1.value() + reactivePowerL2.value() + reactivePowerL3.value();
        -	}
        -
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.symmetric.balancingcurrent;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.meter.AsymmetricMeterNature;
        +import io.openems.api.exception.InvalidValueException;
        +
        +@IsThingMap(type = AsymmetricMeterNature.class)
        +public class Meter extends ThingMap {
        +
        +	public final ReadChannel currentL1;
        +	public final ReadChannel currentL2;
        +	public final ReadChannel currentL3;
        +	public final ReadChannel voltageL1;
        +	public final ReadChannel voltageL2;
        +	public final ReadChannel voltageL3;
        +	public final ReadChannel activePowerL1;
        +	public final ReadChannel reactivePowerL1;
        +	public final ReadChannel activePowerL2;
        +	public final ReadChannel reactivePowerL2;
        +	public final ReadChannel activePowerL3;
        +	public final ReadChannel reactivePowerL3;
        +
        +	public Meter(AsymmetricMeterNature meter) {
        +		super(meter);
        +		currentL1 = meter.currentL1().required();
        +		currentL2 = meter.currentL2().required();
        +		currentL3 = meter.currentL3().required();
        +		voltageL1 = meter.voltageL1().required();
        +		voltageL2 = meter.voltageL2().required();
        +		voltageL3 = meter.voltageL3().required();
        +		activePowerL1 = meter.activePowerL1();
        +		activePowerL2 = meter.activePowerL2();
        +		activePowerL3 = meter.activePowerL3();
        +		reactivePowerL1 = meter.reactivePowerL1();
        +		reactivePowerL2 = meter.reactivePowerL2();
        +		reactivePowerL3 = meter.reactivePowerL3();
        +	}
        +
        +	public Long getActivePower() throws InvalidValueException {
        +		return activePowerL1.value() + activePowerL2.value() + activePowerL3.value();
        +	}
        +
        +	public Long getReactivePower() throws InvalidValueException {
        +		return reactivePowerL1.value() + reactivePowerL2.value() + reactivePowerL3.value();
        +	}
        +
        +}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java
        index ca76d45d23d..f843e40145f 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java
        @@ -1,65 +1,65 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.symmetric.balancingoffset;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.SymmetricPower;
        -
        -@IsThingMap(type = SymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public final ReadChannel minSoc;
        -	public final WriteChannel setActivePower;
        -	public final WriteChannel setReactivePower;
        -	public final ReadChannel soc;
        -	public final ReadChannel activePower;
        -	public final ReadChannel reactivePower;
        -	public final ReadChannel allowedCharge;
        -	public final ReadChannel allowedDischarge;
        -	public final ReadChannel systemState;
        -	public final SymmetricPower power;
        -
        -	public Ess(SymmetricEssNature ess) {
        -		super(ess);
        -		minSoc = ess.minSoc().required();
        -
        -		setActivePower = ess.setActivePower().required();
        -		setReactivePower = ess.setReactivePower().required();
        -
        -		soc = ess.soc().required();
        -		activePower = ess.activePower().required();
        -		allowedCharge = ess.allowedCharge().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		systemState = ess.systemState().required();
        -		reactivePower = ess.reactivePower().required();
        -		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        -				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.symmetric.balancingoffset;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.SymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.core.utilities.SymmetricPower;
        +
        +@IsThingMap(type = SymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public final ReadChannel minSoc;
        +	public final WriteChannel setActivePower;
        +	public final WriteChannel setReactivePower;
        +	public final ReadChannel soc;
        +	public final ReadChannel activePower;
        +	public final ReadChannel reactivePower;
        +	public final ReadChannel allowedCharge;
        +	public final ReadChannel allowedDischarge;
        +	public final ReadChannel systemState;
        +	public final SymmetricPower power;
        +
        +	public Ess(SymmetricEssNature ess) {
        +		super(ess);
        +		minSoc = ess.minSoc().required();
        +
        +		setActivePower = ess.setActivePower().required();
        +		setReactivePower = ess.setReactivePower().required();
        +
        +		soc = ess.soc().required();
        +		activePower = ess.activePower().required();
        +		allowedCharge = ess.allowedCharge().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		systemState = ess.systemState().required();
        +		reactivePower = ess.reactivePower().required();
        +		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        +				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java
        index fd1af55b99d..a864da7b149 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java
        @@ -1,66 +1,66 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.symmetric.balancingsurplus;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.SymmetricPower;
        -
        -@IsThingMap(type = SymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public final ReadChannel minSoc;
        -	public final WriteChannel setActivePower;
        -	public final WriteChannel setReactivePower;
        -	public final ReadChannel soc;
        -	public final ReadChannel activePower;
        -	public final ReadChannel allowedCharge;
        -	public final ReadChannel allowedDischarge;
        -	public final ReadChannel gridMode;
        -	public final ReadChannel systemState;
        -	public final ReadChannel nominalPower;
        -	public final SymmetricPower power;
        -
        -	public Ess(SymmetricEssNature ess) {
        -		super(ess);
        -		minSoc = ess.minSoc().required();
        -
        -		setActivePower = ess.setActivePower().required();
        -		setReactivePower = ess.setReactivePower().required();
        -		this.nominalPower = ess.maxNominalPower().required();
        -		soc = ess.soc().required();
        -		activePower = ess.activePower().required();
        -		allowedCharge = ess.allowedCharge().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		gridMode = ess.gridMode().required();
        -		systemState = ess.systemState().required();
        -		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        -				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.symmetric.balancingsurplus;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.SymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.core.utilities.SymmetricPower;
        +
        +@IsThingMap(type = SymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public final ReadChannel minSoc;
        +	public final WriteChannel setActivePower;
        +	public final WriteChannel setReactivePower;
        +	public final ReadChannel soc;
        +	public final ReadChannel activePower;
        +	public final ReadChannel allowedCharge;
        +	public final ReadChannel allowedDischarge;
        +	public final ReadChannel gridMode;
        +	public final ReadChannel systemState;
        +	public final ReadChannel nominalPower;
        +	public final SymmetricPower power;
        +
        +	public Ess(SymmetricEssNature ess) {
        +		super(ess);
        +		minSoc = ess.minSoc().required();
        +
        +		setActivePower = ess.setActivePower().required();
        +		setReactivePower = ess.setReactivePower().required();
        +		this.nominalPower = ess.maxNominalPower().required();
        +		soc = ess.soc().required();
        +		activePower = ess.activePower().required();
        +		allowedCharge = ess.allowedCharge().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		gridMode = ess.gridMode().required();
        +		systemState = ess.systemState().required();
        +		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        +				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java
        index d9289e1a36f..85b51c1c03d 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java
        @@ -1,57 +1,57 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.symmetric.powerbyfrequency;
        -
        -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.SymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.SymmetricPower;
        -
        -@IsThingMap(type = SymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public final ReadChannel minSoc;
        -	public final ReadChannel soc;
        -	public final ReadChannel activePower;
        -	public final ReadChannel reactivePower;
        -	public final ReadChannel systemState;
        -	public final ReadChannel maxNominalPower;
        -	public final SymmetricPower power;
        -
        -	public Ess(SymmetricEssNature ess) {
        -		super(ess);
        -		minSoc = ess.minSoc().required();
        -
        -		soc = ess.soc().required();
        -		activePower = ess.activePower().required();
        -		systemState = ess.systemState().required();
        -		reactivePower = ess.reactivePower().required();
        -		maxNominalPower = ess.maxNominalPower().required();
        -		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        -				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.symmetric.powerbyfrequency;
        +
        +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.SymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.core.utilities.SymmetricPower;
        +
        +@IsThingMap(type = SymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public final ReadChannel minSoc;
        +	public final ReadChannel soc;
        +	public final ReadChannel activePower;
        +	public final ReadChannel reactivePower;
        +	public final ReadChannel systemState;
        +	public final ReadChannel maxNominalPower;
        +	public final SymmetricPower power;
        +
        +	public Ess(SymmetricEssNature ess) {
        +		super(ess);
        +		minSoc = ess.minSoc().required();
        +
        +		soc = ess.soc().required();
        +		activePower = ess.activePower().required();
        +		systemState = ess.systemState().required();
        +		reactivePower = ess.reactivePower().required();
        +		maxNominalPower = ess.maxNominalPower().required();
        +		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        +				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java
        index ff50b6225e5..229647fabd3 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java
        @@ -42,12 +42,13 @@
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.api.exception.ReflectionException;
         import io.openems.api.exception.WriteChannelException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.session.Role;
        +import io.openems.common.utils.JsonUtils;
         import io.openems.core.utilities.AvgFiFoQueue;
         import io.openems.core.utilities.ControllerUtils;
        -import io.openems.core.utilities.JsonUtils;
        +
         
         @ThingInfo(title = "Timeline charge (Symmetric)")
         public class TimelineChargeController extends Controller {
        @@ -375,7 +376,7 @@ private Entry floorSoc(JsonArray jHours, LocalTime time) thr
         			} else {
         				throw new IndexOutOfBoundsException("No smaller time found");
         			}
        -		} catch (ReflectionException e) {
        +		} catch (OpenemsException e) {
         			throw new ConfigException("cant read config", e);
         		}
         	}
        @@ -396,7 +397,7 @@ private Map.Entry higherSoc(JsonArray jHours, LocalTime time
         			} else {
         				throw new IndexOutOfBoundsException("No smaller time found");
         			}
        -		} catch (ReflectionException e) {
        +		} catch (OpenemsException e) {
         			throw new ConfigException("cant read config", e);
         		}
         	}
        diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java
        index 6d941d5257f..e926edc7c85 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java
        @@ -1,67 +1,67 @@
        -/*******************************************************************************
        - * OpenEMS - Open Source Energy Management System
        - * Copyright (c) 2016 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.symmetric.voltagecharacteristic;
        -
        -import io.openems.api.channel.ReadChannel;
        -import io.openems.api.channel.WriteChannel;
        -import io.openems.api.controller.IsThingMap;
        -import io.openems.api.controller.ThingMap;
        -import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.SymmetricPower;
        -
        -@IsThingMap(type = SymmetricEssNature.class)
        -public class Ess extends ThingMap {
        -
        -	public final ReadChannel minSoc;
        -	public final WriteChannel setActivePower;
        -	public final WriteChannel setReactivePower;
        -	public final ReadChannel soc;
        -	public final ReadChannel activePower;
        -	public final ReadChannel reactivePower;
        -	public final ReadChannel allowedCharge;
        -	public final ReadChannel allowedDischarge;
        -	public final ReadChannel systemState;
        -	public final ReadChannel maxNominalPower;
        -	public final SymmetricPower power;
        -
        -	public Ess(SymmetricEssNature ess) {
        -		super(ess);
        -		minSoc = ess.minSoc().required();
        -
        -		setActivePower = ess.setActivePower().required();
        -		setReactivePower = ess.setReactivePower().required();
        -
        -		soc = ess.soc().required();
        -		activePower = ess.activePower().required();
        -		allowedCharge = ess.allowedCharge().required();
        -		allowedDischarge = ess.allowedDischarge().required();
        -		systemState = ess.systemState().required();
        -		reactivePower = ess.reactivePower().required();
        -		maxNominalPower = ess.maxNominalPower().required();
        -		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        -				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        -	}
        -
        -	public long useableSoc() throws InvalidValueException {
        -		return soc.value() - minSoc.value();
        -	}
        -}
        +/*******************************************************************************
        + * OpenEMS - Open Source Energy Management System
        + * Copyright (c) 2016 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.symmetric.voltagecharacteristic;
        +
        +import io.openems.api.channel.ReadChannel;
        +import io.openems.api.channel.WriteChannel;
        +import io.openems.api.controller.IsThingMap;
        +import io.openems.api.controller.ThingMap;
        +import io.openems.api.device.nature.ess.SymmetricEssNature;
        +import io.openems.api.exception.InvalidValueException;
        +import io.openems.core.utilities.SymmetricPower;
        +
        +@IsThingMap(type = SymmetricEssNature.class)
        +public class Ess extends ThingMap {
        +
        +	public final ReadChannel minSoc;
        +	public final WriteChannel setActivePower;
        +	public final WriteChannel setReactivePower;
        +	public final ReadChannel soc;
        +	public final ReadChannel activePower;
        +	public final ReadChannel reactivePower;
        +	public final ReadChannel allowedCharge;
        +	public final ReadChannel allowedDischarge;
        +	public final ReadChannel systemState;
        +	public final ReadChannel maxNominalPower;
        +	public final SymmetricPower power;
        +
        +	public Ess(SymmetricEssNature ess) {
        +		super(ess);
        +		minSoc = ess.minSoc().required();
        +
        +		setActivePower = ess.setActivePower().required();
        +		setReactivePower = ess.setReactivePower().required();
        +
        +		soc = ess.soc().required();
        +		activePower = ess.activePower().required();
        +		allowedCharge = ess.allowedCharge().required();
        +		allowedDischarge = ess.allowedDischarge().required();
        +		systemState = ess.systemState().required();
        +		reactivePower = ess.reactivePower().required();
        +		maxNominalPower = ess.maxNominalPower().required();
        +		this.power = new SymmetricPower(ess.allowedDischarge().required(), ess.allowedCharge().required(),
        +				ess.allowedApparent().required(), ess.setActivePower().required(), ess.setReactivePower().required());
        +	}
        +
        +	public long useableSoc() throws InvalidValueException {
        +		return soc.value() - minSoc.value();
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/device/bcontrol/BControl.java b/edge/src/io/openems/impl/device/bcontrol/BControl.java
        index 2512607fe05..d901555c1c4 100644
        --- a/edge/src/io/openems/impl/device/bcontrol/BControl.java
        +++ b/edge/src/io/openems/impl/device/bcontrol/BControl.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "B-Control Energy Meter")
        diff --git a/edge/src/io/openems/impl/device/byd/Bem125ktla01.java b/edge/src/io/openems/impl/device/byd/Bem125ktla01.java
        index 08d454ea019..9c21c130e04 100644
        --- a/edge/src/io/openems/impl/device/byd/Bem125ktla01.java
        +++ b/edge/src/io/openems/impl/device/byd/Bem125ktla01.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         /*
        diff --git a/edge/src/io/openems/impl/device/carlogavazzi/em300series/EM300.java b/edge/src/io/openems/impl/device/carlogavazzi/em300series/EM300.java
        index 728f6398046..c9f84010c23 100644
        --- a/edge/src/io/openems/impl/device/carlogavazzi/em300series/EM300.java
        +++ b/edge/src/io/openems/impl/device/carlogavazzi/em300series/EM300.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Carlog Gavazzi EM300")
        diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialAC.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialAC.java
        index d4efd99fa95..2a13b356592 100644
        --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialAC.java
        +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialAC.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "FENECON Commercial AC")
        diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialDC.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialDC.java
        index aeb8d21aeb5..438d2e1f0f9 100644
        --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialDC.java
        +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialDC.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "FENECON Commercial DC/Hybrid")
        diff --git a/edge/src/io/openems/impl/device/custom/riedmann/Riedmann.java b/edge/src/io/openems/impl/device/custom/riedmann/Riedmann.java
        index 25c7be93058..befd52d5097 100644
        --- a/edge/src/io/openems/impl/device/custom/riedmann/Riedmann.java
        +++ b/edge/src/io/openems/impl/device/custom/riedmann/Riedmann.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Custom: Riedmann PLC")
        diff --git a/edge/src/io/openems/impl/device/janitza/JanitzaUMG96RME.java b/edge/src/io/openems/impl/device/janitza/JanitzaUMG96RME.java
        index 7a8168ad9a0..1b50aeb1547 100644
        --- a/edge/src/io/openems/impl/device/janitza/JanitzaUMG96RME.java
        +++ b/edge/src/io/openems/impl/device/janitza/JanitzaUMG96RME.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Janitza UMG96RM E")
        diff --git a/edge/src/io/openems/impl/device/kmtronic/KMTronicRelay.java b/edge/src/io/openems/impl/device/kmtronic/KMTronicRelay.java
        index 71b47108e9d..a0fb92a2711 100644
        --- a/edge/src/io/openems/impl/device/kmtronic/KMTronicRelay.java
        +++ b/edge/src/io/openems/impl/device/kmtronic/KMTronicRelay.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "KMTronic Relay board")
        diff --git a/edge/src/io/openems/impl/device/kmtronic/KMTronicRelayRev1.java b/edge/src/io/openems/impl/device/kmtronic/KMTronicRelayRev1.java
        index 4062af41254..6ed422392ec 100644
        --- a/edge/src/io/openems/impl/device/kmtronic/KMTronicRelayRev1.java
        +++ b/edge/src/io/openems/impl/device/kmtronic/KMTronicRelayRev1.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "KMTronic Relay board")
        diff --git a/edge/src/io/openems/impl/device/mini/FeneconMini.java b/edge/src/io/openems/impl/device/mini/FeneconMini.java
        index 6bf6f34848f..bdae8af65ec 100644
        --- a/edge/src/io/openems/impl/device/mini/FeneconMini.java
        +++ b/edge/src/io/openems/impl/device/mini/FeneconMini.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "FENECON Mini")
        diff --git a/edge/src/io/openems/impl/device/minireadonly/FeneconMini.java b/edge/src/io/openems/impl/device/minireadonly/FeneconMini.java
        index feb3f4eeeae..90c5746ce5d 100644
        --- a/edge/src/io/openems/impl/device/minireadonly/FeneconMini.java
        +++ b/edge/src/io/openems/impl/device/minireadonly/FeneconMini.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "FENECON Mini")
        diff --git a/edge/src/io/openems/impl/device/pqplus/PqPlusUMD97.java b/edge/src/io/openems/impl/device/pqplus/PqPlusUMD97.java
        index e902deacf8f..0ca83574e45 100644
        --- a/edge/src/io/openems/impl/device/pqplus/PqPlusUMD97.java
        +++ b/edge/src/io/openems/impl/device/pqplus/PqPlusUMD97.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "PQ Plus UMD 97")
        diff --git a/edge/src/io/openems/impl/device/pro/FeneconPro.java b/edge/src/io/openems/impl/device/pro/FeneconPro.java
        index 8b239fa2daa..e81234f279b 100644
        --- a/edge/src/io/openems/impl/device/pro/FeneconPro.java
        +++ b/edge/src/io/openems/impl/device/pro/FeneconPro.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "FENECON Pro")
        diff --git a/edge/src/io/openems/impl/device/refu/Refu.java b/edge/src/io/openems/impl/device/refu/Refu.java
        index 510eede1aa9..19dc83a17d0 100644
        --- a/edge/src/io/openems/impl/device/refu/Refu.java
        +++ b/edge/src/io/openems/impl/device/refu/Refu.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "REFU battery inverter")
        diff --git a/edge/src/io/openems/impl/device/simulator/Simulator.java b/edge/src/io/openems/impl/device/simulator/Simulator.java
        index 0cf07affe22..e9cb5fe1239 100644
        --- a/edge/src/io/openems/impl/device/simulator/Simulator.java
        +++ b/edge/src/io/openems/impl/device/simulator/Simulator.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.simulator.SimulatorDevice;
         
         @ThingInfo(title = "Simulator")
        diff --git a/edge/src/io/openems/impl/device/sma/SunnyIsland6.java b/edge/src/io/openems/impl/device/sma/SunnyIsland6.java
        index 11acedeb328..e0424937ded 100644
        --- a/edge/src/io/openems/impl/device/sma/SunnyIsland6.java
        +++ b/edge/src/io/openems/impl/device/sma/SunnyIsland6.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title="SMA SunnyIsland 6.0H")
        diff --git a/edge/src/io/openems/impl/device/socomec/Socomec.java b/edge/src/io/openems/impl/device/socomec/Socomec.java
        index fa174b9dd3b..dbbef21d11a 100644
        --- a/edge/src/io/openems/impl/device/socomec/Socomec.java
        +++ b/edge/src/io/openems/impl/device/socomec/Socomec.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Socomec")
        diff --git a/edge/src/io/openems/impl/device/socomec/SocomecB30.java b/edge/src/io/openems/impl/device/socomec/SocomecB30.java
        index 7b2d8a09f80..91e4def95cd 100644
        --- a/edge/src/io/openems/impl/device/socomec/SocomecB30.java
        +++ b/edge/src/io/openems/impl/device/socomec/SocomecB30.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Socomec B30")
        diff --git a/edge/src/io/openems/impl/device/socomec/SocomecSinglePhase.java b/edge/src/io/openems/impl/device/socomec/SocomecSinglePhase.java
        index e0d11d28283..5091acf0d0c 100644
        --- a/edge/src/io/openems/impl/device/socomec/SocomecSinglePhase.java
        +++ b/edge/src/io/openems/impl/device/socomec/SocomecSinglePhase.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Socomec")
        diff --git a/edge/src/io/openems/impl/device/spanner/BHKW.java b/edge/src/io/openems/impl/device/spanner/BHKW.java
        index 822f1c38e63..291d29d129c 100644
        --- a/edge/src/io/openems/impl/device/spanner/BHKW.java
        +++ b/edge/src/io/openems/impl/device/spanner/BHKW.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "Spanner BHKW")
        diff --git a/edge/src/io/openems/impl/device/streetscooter/Streetscooter.java b/edge/src/io/openems/impl/device/streetscooter/Streetscooter.java
        index e98f01d5b7d..357816a097f 100644
        --- a/edge/src/io/openems/impl/device/streetscooter/Streetscooter.java
        +++ b/edge/src/io/openems/impl/device/streetscooter/Streetscooter.java
        @@ -28,7 +28,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         @ThingInfo(title = "FENECON Pro")
        diff --git a/edge/src/io/openems/impl/device/studer/StuderVs70.java b/edge/src/io/openems/impl/device/studer/StuderVs70.java
        index ffbc9eb2b35..e777dece31f 100644
        --- a/edge/src/io/openems/impl/device/studer/StuderVs70.java
        +++ b/edge/src/io/openems/impl/device/studer/StuderVs70.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.studer.StuderDevice;
         
         @ThingInfo(title = "Studer VS-70")
        diff --git a/edge/src/io/openems/impl/device/system/System.java b/edge/src/io/openems/impl/device/system/System.java
        index 6ddaefdf2d9..bdf710487e6 100644
        --- a/edge/src/io/openems/impl/device/system/System.java
        +++ b/edge/src/io/openems/impl/device/system/System.java
        @@ -32,7 +32,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.system.SystemDevice;
         
         @ThingInfo(title = "Operating system")
        diff --git a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java
        index 1ce4f9d9560..7d29fb414a5 100644
        --- a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java
        +++ b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.system.SystemDevice;
         
         @ThingInfo(title = "Ess Asymmetric-Symmetric-Combination")
        diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java b/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java
        index d1755ee83d3..6d8022bf976 100644
        --- a/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java
        +++ b/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.system.SystemDevice;
         
         @ThingInfo(title = "Ess Cluster")
        diff --git a/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java b/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java
        index d4f4d6ba76b..7c496bd2e8c 100644
        --- a/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java
        +++ b/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java
        @@ -8,7 +8,7 @@
         import io.openems.api.device.nature.DeviceNature;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.system.SystemDevice;
         
         @ThingInfo(title = "Meter Cluster")
        diff --git a/edge/src/io/openems/impl/device/wago/WagoFB.java b/edge/src/io/openems/impl/device/wago/WagoFB.java
        index 62219f84c3d..8e182945fa5 100644
        --- a/edge/src/io/openems/impl/device/wago/WagoFB.java
        +++ b/edge/src/io/openems/impl/device/wago/WagoFB.java
        @@ -47,7 +47,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.impl.protocol.modbus.ModbusDevice;
         
         /*
        diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java
        index 7d101310dfc..5a78f43cf4f 100644
        --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java
        +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java
        @@ -46,7 +46,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
        -import io.openems.api.exception.NotImplementedException;
        +import io.openems.common.exceptions.NotImplementedException;
         import io.openems.api.persistence.Persistence;
         import io.openems.api.thing.Thing;
         import io.openems.common.session.Role;
        diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java
        index 6793721a7e7..86828de7ba4 100644
        --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java
        +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java
        @@ -42,7 +42,7 @@
         import io.openems.api.channel.thingstate.ThingStateChannels;
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
        -import io.openems.api.exception.OpenemsException;
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.api.persistence.QueryablePersistence;
         import io.openems.backend.timedata.influx.InfluxdbUtils;
         import io.openems.common.types.ChannelEnum;
        diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java
        index b26acd3b96f..80cd0b5ac09 100644
        --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java
        +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java
        @@ -1,349 +1,349 @@
        -package io.openems.impl.persistence.influxdb;
        -
        -import java.time.Instant;
        -import java.time.ZonedDateTime;
        -import java.time.format.DateTimeFormatter;
        -import java.time.temporal.ChronoUnit;
        -import java.util.ArrayList;
        -import java.util.List;
        -import java.util.Map.Entry;
        -import java.util.Optional;
        -import java.util.concurrent.TimeUnit;
        -
        -import org.influxdb.InfluxDB;
        -import org.influxdb.dto.Query;
        -import org.influxdb.dto.QueryResult;
        -import org.influxdb.dto.QueryResult.Result;
        -import org.influxdb.dto.QueryResult.Series;
        -
        -import com.google.gson.JsonArray;
        -import com.google.gson.JsonElement;
        -import com.google.gson.JsonNull;
        -import com.google.gson.JsonObject;
        -
        -import io.openems.api.exception.OpenemsException;
        -import io.openems.core.Address;
        -import io.openems.core.utilities.JsonUtils;
        -
        -public class InfluxdbQueryWrapper {
        -
        -	// private final static Logger log = LoggerFactory.getLogger(InfluxdbQueryWrapper.class);
        -
        -	public static JsonObject query(Optional _influxdb, Optional fems, ZonedDateTime fromDate,
        -			ZonedDateTime toDate, JsonObject channels, int resolution, String dbName) throws OpenemsException {
        -		// Prepare return object
        -		JsonObject jQueryreply = new JsonObject();
        -		jQueryreply.addProperty("mode", "history");
        -		JsonArray jData = new JsonArray();
        -		JsonObject jkWh = new JsonObject();
        -
        -		// Prepare date
        -		toDate = toDate.plusDays(1).truncatedTo(ChronoUnit.DAYS);
        -
        -		ZonedDateTime nowDate = ZonedDateTime.now();
        -		if (nowDate.isBefore(toDate)) {
        -			toDate = nowDate;
        -		}
        -		if (fromDate.isAfter(toDate)) {
        -			fromDate = toDate;
        -		}
        -
        -		if (_influxdb.isPresent()) {
        -			InfluxDB influxdb = _influxdb.get();
        -			jData = InfluxdbQueryWrapper.queryData(influxdb, fems, fromDate, toDate, channels, resolution, dbName);
        -			// TODO jkWh = InfluxdbQueryWrapper.querykWh(influxdb, fems, fromDate, toDate, channels, resolution, kWh,
        -			// dbName);
        -		} else {
        -			jData = new JsonArray();
        -			jkWh = new JsonObject();
        -		}
        -		jQueryreply.add("data", jData);
        -		jQueryreply.add("kWh", jkWh);
        -		return jQueryreply;
        -	}
        -
        -	private static JsonArray queryData(InfluxDB influxdb, Optional fems, ZonedDateTime fromDate,
        -			ZonedDateTime toDate, JsonObject channels, int resolution, String dbName) throws OpenemsException {
        -		// Prepare query string
        -		StringBuilder query = new StringBuilder("SELECT ");
        -		query.append(toChannelAddressList(channels));
        -		query.append(" FROM data WHERE ");
        -		if (fems.isPresent()) {
        -			query.append("fems = '");
        -			query.append(fems.get());
        -			query.append("' AND ");
        -		}
        -		query.append("time > ");
        -		query.append(String.valueOf(fromDate.toEpochSecond()));
        -		query.append("s");
        -		query.append(" AND time < ");
        -		query.append(String.valueOf(toDate.toEpochSecond()));
        -		query.append("s");
        -		query.append(" GROUP BY time(");
        -		query.append(resolution);
        -		query.append("s) fill(previous)");
        -
        -		QueryResult queryResult = executeQuery(influxdb, query.toString(), dbName);
        -
        -		JsonArray j = new JsonArray();
        -		for (Result result : queryResult.getResults()) {
        -			List seriess = result.getSeries();
        -			if (seriess != null) {
        -				for (Series series : seriess) {
        -					// create thing/channel index
        -					ArrayList
        addressIndex = new ArrayList<>(); - for (String column : series.getColumns()) { - if (column.equals("time")) { - continue; - } - addressIndex.add(Address.fromString(column)); - } - // first: create empty timestamp objects - for (List values : series.getValues()) { - JsonObject jTimestamp = new JsonObject(); - // get timestamp - Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) values.get(0)).doubleValue()); - ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); - String timestampString = timestamp.format(DateTimeFormatter.ISO_INSTANT); - jTimestamp.addProperty("time", timestampString); - // add empty channels by copying "channels" parameter - JsonObject jChannels = new JsonObject(); - for (Entry entry : channels.entrySet()) { - String thingId = entry.getKey(); - JsonObject jThing = new JsonObject(); - JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement channelElement : channelIds) { - String channelId = JsonUtils.getAsString(channelElement); - jThing.add(channelId, JsonNull.INSTANCE); - } - jChannels.add(thingId, jThing); - } - jTimestamp.add("channels", jChannels); - j.add(jTimestamp); - } - // then: add all data - for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { - for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { - Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); - Address address = addressIndex.get(columnIndex - 1); - j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() - .get(address.getThingId()).getAsJsonObject() - .addProperty(address.getChannelId(), value); - } - } - } - } - } - return j; - } - - // TODO - // private static JsonObject querykWh(InfluxDB influxdb, Optional fems, ZonedDateTime fromDate, - // ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh, String dbName) - // throws OpenemsException { - // JsonArray gridThing = getGridThing(kWh); - // JsonArray storageThing = getStorageThing(kWh); - // JsonArray things = new JsonArray(); - // things.addAll(storageThing); - // things.addAll(gridThing); - // - // JsonObject jThing = new JsonObject(); - // ArrayList productionChannels = toChannelAddressListAvg(channels, things); - // - // for (int i = 0; i < productionChannels.size(); i++) { - // /* - // * SUM data - // */ - // StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); - // query.append(productionChannels.get(i)); - // query.append("\") AS AP FROM data WHERE "); - // if (fems.isPresent()) { - // query.append("fems = '"); - // query.append(fems.get()); - // query.append("' AND "); - // } - // query.append("time > "); - // query.append(String.valueOf(fromDate.toEpochSecond())); - // query.append("s"); - // query.append(" AND time < "); - // query.append(String.valueOf(toDate.toEpochSecond())); - // query.append("s"); - // query.append(" GROUP BY time(1s) fill(previous))"); - // - // QueryResult queryResult = executeQuery(influxdb, query.toString(), dbName); - // - // Double sumProduction = 0.0; - // try { - // for (Result result : queryResult.getResults()) { - // for (Series serie : result.getSeries()) { - // for (List l : serie.getValues()) { - // sumProduction = (Double) l.get(1); - // } - // } - // } - // } catch (Exception e) { - // log.warn("Error parsing SUM production: " + e); - // } - // - // /* - // * FIRST production data - // */ - // query = new StringBuilder("SELECT FIRST(\""); - // query.append(productionChannels.get(i)); - // query.append("\") FROM data WHERE "); - // if (fems.isPresent()) { - // query.append("fems = '"); - // query.append(fems.get()); - // query.append("' AND "); - // } - // query.append("time > "); - // query.append(String.valueOf(fromDate.toEpochSecond())); - // query.append("s"); - // query.append(" AND time < "); - // query.append(String.valueOf(toDate.toEpochSecond())); - // query.append("s"); - // - // queryResult = executeQuery(influxdb, query.toString(), dbName); - // - // int second = 0; - // try { - // for (Result result : queryResult.getResults()) { - // for (Series serie : result.getSeries()) { - // for (List l : serie.getValues()) { - // Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); - // ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); - // if (timestamp.equals(fromDate)) { - // log.info("Parsing FIRST: nothing null"); - // } else { - // second = timestamp.getSecond(); - // } - // } - // } - // } - // } catch (Exception e) { - // log.warn("Error parsing FIRST production: " + e); - // } - // - // /* - // * LAST data - // */ - // query = new StringBuilder("SELECT LAST(\""); - // query.append(productionChannels.get(i)); - // query.append("\") FROM data WHERE "); - // if (fems.isPresent()) { - // query.append("fems = '"); - // query.append(fems.get()); - // query.append("' AND "); - // } - // query.append("time < "); - // query.append(String.valueOf(fromDate.toEpochSecond())); - // query.append("s"); - // - // queryResult = executeQuery(influxdb, query.toString(), dbName); - // - // try { - // if (queryResult.getResults() != null) { - // for (Result result : queryResult.getResults()) { - // if (result.getSeries() != null) { - // for (Series serie : result.getSeries()) { - // if (serie.getValues() != null) { - // for (List l : serie.getValues()) { - // if (l.get(1) != null) { - // sumProduction += (Double) l.get(1) * second; - // } - // } - // } - // } - // } - // } - // } - // } catch (Exception e) { - // log.warn("Error parsing LAST production: " + e); - // } - // - // Double avg = sumProduction / 3600 / 1000; - // - // JsonObject element = new JsonObject(); - // element.addProperty("value", avg); - // element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); - // jThing.add(productionChannels.get(i).toString(), element); - // } - // - // return jThing; - // } - - // private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { - // JsonArray gridThing = new JsonArray(); - // for (Entry entry : kWh.entrySet()) { - // String thingId = entry.getKey(); - // if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { - // gridThing.add(thingId); - // } - // } - // return gridThing; - // } - - // private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { - // JsonArray storageThing = new JsonArray(); - // for (Entry entry : kWh.entrySet()) { - // String thingId = entry.getKey(); - // if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { - // storageThing.add(thingId); - // } - // } - // return storageThing; - // } - - // private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) - // throws OpenemsException { - // ArrayList channelAddresses = new ArrayList<>(); - // for (Entry entry : channels.entrySet()) { - // String thingId = entry.getKey(); - // JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - // for (JsonElement channelElement : channelIds) { - // String channelId = JsonUtils.getAsString(channelElement); - // if (channelId.contains("ActivePower")) { - // String name = thingId + "/" + channelId; - // boolean isGridOrStorage = false; - // for (int i = 0; i < things.size(); i++) { - // if (JsonUtils.getAsString(things.get(i)).equals(name)) { - // isGridOrStorage = true; - // } - // } - // if (!isGridOrStorage) { - // channelAddresses.add(thingId + "/" + channelId); - // } - // } - // } - // } - // return channelAddresses; - // } - - private static String toChannelAddressList(JsonObject channels) throws OpenemsException { - ArrayList channelAddresses = new ArrayList<>(); - for (Entry entry : channels.entrySet()) { - String thingId = entry.getKey(); - JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement channelElement : channelIds) { - String channelId = JsonUtils.getAsString(channelElement); - channelAddresses - .add("MEAN(\"" + thingId + "/" + channelId + "\") AS \"" + thingId + "/" + channelId + "\""); - } - } - return String.join(", ", channelAddresses); - } - - private static QueryResult executeQuery(InfluxDB influxdb, String query, String dbName) throws OpenemsException { - // Parse result - QueryResult queryResult; - try { - queryResult = influxdb.query(new Query(query, dbName), TimeUnit.MILLISECONDS); - } catch (RuntimeException e) { - throw new OpenemsException("InfluxDB query runtime error: " + e.getMessage()); - } - if (queryResult.hasError()) { - throw new OpenemsException("InfluxDB query error: " + queryResult.getError()); - } - return queryResult; - } -} +package io.openems.impl.persistence.influxdb; + +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.influxdb.InfluxDB; +import org.influxdb.dto.Query; +import org.influxdb.dto.QueryResult; +import org.influxdb.dto.QueryResult.Result; +import org.influxdb.dto.QueryResult.Series; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; +import io.openems.core.Address; + +public class InfluxdbQueryWrapper { + + // private final static Logger log = LoggerFactory.getLogger(InfluxdbQueryWrapper.class); + + public static JsonObject query(Optional _influxdb, Optional fems, ZonedDateTime fromDate, + ZonedDateTime toDate, JsonObject channels, int resolution, String dbName) throws OpenemsException { + // Prepare return object + JsonObject jQueryreply = new JsonObject(); + jQueryreply.addProperty("mode", "history"); + JsonArray jData = new JsonArray(); + JsonObject jkWh = new JsonObject(); + + // Prepare date + toDate = toDate.plusDays(1).truncatedTo(ChronoUnit.DAYS); + + ZonedDateTime nowDate = ZonedDateTime.now(); + if (nowDate.isBefore(toDate)) { + toDate = nowDate; + } + if (fromDate.isAfter(toDate)) { + fromDate = toDate; + } + + if (_influxdb.isPresent()) { + InfluxDB influxdb = _influxdb.get(); + jData = InfluxdbQueryWrapper.queryData(influxdb, fems, fromDate, toDate, channels, resolution, dbName); + // TODO jkWh = InfluxdbQueryWrapper.querykWh(influxdb, fems, fromDate, toDate, channels, resolution, kWh, + // dbName); + } else { + jData = new JsonArray(); + jkWh = new JsonObject(); + } + jQueryreply.add("data", jData); + jQueryreply.add("kWh", jkWh); + return jQueryreply; + } + + private static JsonArray queryData(InfluxDB influxdb, Optional fems, ZonedDateTime fromDate, + ZonedDateTime toDate, JsonObject channels, int resolution, String dbName) throws OpenemsException { + // Prepare query string + StringBuilder query = new StringBuilder("SELECT "); + query.append(toChannelAddressList(channels)); + query.append(" FROM data WHERE "); + if (fems.isPresent()) { + query.append("fems = '"); + query.append(fems.get()); + query.append("' AND "); + } + query.append("time > "); + query.append(String.valueOf(fromDate.toEpochSecond())); + query.append("s"); + query.append(" AND time < "); + query.append(String.valueOf(toDate.toEpochSecond())); + query.append("s"); + query.append(" GROUP BY time("); + query.append(resolution); + query.append("s) fill(previous)"); + + QueryResult queryResult = executeQuery(influxdb, query.toString(), dbName); + + JsonArray j = new JsonArray(); + for (Result result : queryResult.getResults()) { + List seriess = result.getSeries(); + if (seriess != null) { + for (Series series : seriess) { + // create thing/channel index + ArrayList
        addressIndex = new ArrayList<>(); + for (String column : series.getColumns()) { + if (column.equals("time")) { + continue; + } + addressIndex.add(Address.fromString(column)); + } + // first: create empty timestamp objects + for (List values : series.getValues()) { + JsonObject jTimestamp = new JsonObject(); + // get timestamp + Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) values.get(0)).doubleValue()); + ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); + String timestampString = timestamp.format(DateTimeFormatter.ISO_INSTANT); + jTimestamp.addProperty("time", timestampString); + // add empty channels by copying "channels" parameter + JsonObject jChannels = new JsonObject(); + for (Entry entry : channels.entrySet()) { + String thingId = entry.getKey(); + JsonObject jThing = new JsonObject(); + JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement channelElement : channelIds) { + String channelId = JsonUtils.getAsString(channelElement); + jThing.add(channelId, JsonNull.INSTANCE); + } + jChannels.add(thingId, jThing); + } + jTimestamp.add("channels", jChannels); + j.add(jTimestamp); + } + // then: add all data + for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { + for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { + Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); + Address address = addressIndex.get(columnIndex - 1); + j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() + .get(address.getThingId()).getAsJsonObject() + .addProperty(address.getChannelId(), value); + } + } + } + } + } + return j; + } + + // TODO + // private static JsonObject querykWh(InfluxDB influxdb, Optional fems, ZonedDateTime fromDate, + // ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh, String dbName) + // throws OpenemsException { + // JsonArray gridThing = getGridThing(kWh); + // JsonArray storageThing = getStorageThing(kWh); + // JsonArray things = new JsonArray(); + // things.addAll(storageThing); + // things.addAll(gridThing); + // + // JsonObject jThing = new JsonObject(); + // ArrayList productionChannels = toChannelAddressListAvg(channels, things); + // + // for (int i = 0; i < productionChannels.size(); i++) { + // /* + // * SUM data + // */ + // StringBuilder query = new StringBuilder("SELECT SUM(AP) FROM (SELECT MEAN(\""); + // query.append(productionChannels.get(i)); + // query.append("\") AS AP FROM data WHERE "); + // if (fems.isPresent()) { + // query.append("fems = '"); + // query.append(fems.get()); + // query.append("' AND "); + // } + // query.append("time > "); + // query.append(String.valueOf(fromDate.toEpochSecond())); + // query.append("s"); + // query.append(" AND time < "); + // query.append(String.valueOf(toDate.toEpochSecond())); + // query.append("s"); + // query.append(" GROUP BY time(1s) fill(previous))"); + // + // QueryResult queryResult = executeQuery(influxdb, query.toString(), dbName); + // + // Double sumProduction = 0.0; + // try { + // for (Result result : queryResult.getResults()) { + // for (Series serie : result.getSeries()) { + // for (List l : serie.getValues()) { + // sumProduction = (Double) l.get(1); + // } + // } + // } + // } catch (Exception e) { + // log.warn("Error parsing SUM production: " + e); + // } + // + // /* + // * FIRST production data + // */ + // query = new StringBuilder("SELECT FIRST(\""); + // query.append(productionChannels.get(i)); + // query.append("\") FROM data WHERE "); + // if (fems.isPresent()) { + // query.append("fems = '"); + // query.append(fems.get()); + // query.append("' AND "); + // } + // query.append("time > "); + // query.append(String.valueOf(fromDate.toEpochSecond())); + // query.append("s"); + // query.append(" AND time < "); + // query.append(String.valueOf(toDate.toEpochSecond())); + // query.append("s"); + // + // queryResult = executeQuery(influxdb, query.toString(), dbName); + // + // int second = 0; + // try { + // for (Result result : queryResult.getResults()) { + // for (Series serie : result.getSeries()) { + // for (List l : serie.getValues()) { + // Instant timestampInstant = Instant.ofEpochMilli((long) ((Double) l.get(0)).doubleValue()); + // ZonedDateTime timestamp = ZonedDateTime.ofInstant(timestampInstant, fromDate.getZone()); + // if (timestamp.equals(fromDate)) { + // log.info("Parsing FIRST: nothing null"); + // } else { + // second = timestamp.getSecond(); + // } + // } + // } + // } + // } catch (Exception e) { + // log.warn("Error parsing FIRST production: " + e); + // } + // + // /* + // * LAST data + // */ + // query = new StringBuilder("SELECT LAST(\""); + // query.append(productionChannels.get(i)); + // query.append("\") FROM data WHERE "); + // if (fems.isPresent()) { + // query.append("fems = '"); + // query.append(fems.get()); + // query.append("' AND "); + // } + // query.append("time < "); + // query.append(String.valueOf(fromDate.toEpochSecond())); + // query.append("s"); + // + // queryResult = executeQuery(influxdb, query.toString(), dbName); + // + // try { + // if (queryResult.getResults() != null) { + // for (Result result : queryResult.getResults()) { + // if (result.getSeries() != null) { + // for (Series serie : result.getSeries()) { + // if (serie.getValues() != null) { + // for (List l : serie.getValues()) { + // if (l.get(1) != null) { + // sumProduction += (Double) l.get(1) * second; + // } + // } + // } + // } + // } + // } + // } + // } catch (Exception e) { + // log.warn("Error parsing LAST production: " + e); + // } + // + // Double avg = sumProduction / 3600 / 1000; + // + // JsonObject element = new JsonObject(); + // element.addProperty("value", avg); + // element.addProperty("type", JsonUtils.getAsString(kWh.get(productionChannels.get(i)))); + // jThing.add(productionChannels.get(i).toString(), element); + // } + // + // return jThing; + // } + + // private static JsonArray getGridThing(JsonObject kWh) throws OpenemsException { + // JsonArray gridThing = new JsonArray(); + // for (Entry entry : kWh.entrySet()) { + // String thingId = entry.getKey(); + // if (JsonUtils.getAsString(entry.getValue()).equals("grid")) { + // gridThing.add(thingId); + // } + // } + // return gridThing; + // } + + // private static JsonArray getStorageThing(JsonObject kWh) throws OpenemsException { + // JsonArray storageThing = new JsonArray(); + // for (Entry entry : kWh.entrySet()) { + // String thingId = entry.getKey(); + // if (JsonUtils.getAsString(entry.getValue()).equals("storage")) { + // storageThing.add(thingId); + // } + // } + // return storageThing; + // } + + // private static ArrayList toChannelAddressListAvg(JsonObject channels, JsonArray things) + // throws OpenemsException { + // ArrayList channelAddresses = new ArrayList<>(); + // for (Entry entry : channels.entrySet()) { + // String thingId = entry.getKey(); + // JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + // for (JsonElement channelElement : channelIds) { + // String channelId = JsonUtils.getAsString(channelElement); + // if (channelId.contains("ActivePower")) { + // String name = thingId + "/" + channelId; + // boolean isGridOrStorage = false; + // for (int i = 0; i < things.size(); i++) { + // if (JsonUtils.getAsString(things.get(i)).equals(name)) { + // isGridOrStorage = true; + // } + // } + // if (!isGridOrStorage) { + // channelAddresses.add(thingId + "/" + channelId); + // } + // } + // } + // } + // return channelAddresses; + // } + + private static String toChannelAddressList(JsonObject channels) throws OpenemsException { + ArrayList channelAddresses = new ArrayList<>(); + for (Entry entry : channels.entrySet()) { + String thingId = entry.getKey(); + JsonArray channelIds = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement channelElement : channelIds) { + String channelId = JsonUtils.getAsString(channelElement); + channelAddresses + .add("MEAN(\"" + thingId + "/" + channelId + "\") AS \"" + thingId + "/" + channelId + "\""); + } + } + return String.join(", ", channelAddresses); + } + + private static QueryResult executeQuery(InfluxDB influxdb, String query, String dbName) throws OpenemsException { + // Parse result + QueryResult queryResult; + try { + queryResult = influxdb.query(new Query(query, dbName), TimeUnit.MILLISECONDS); + } catch (RuntimeException e) { + throw new OpenemsException("InfluxDB query runtime error: " + e.getMessage()); + } + if (queryResult.hasError()) { + throw new OpenemsException("InfluxDB query error: " + queryResult.getError()); + } + return queryResult; + } +} diff --git a/edge/src/io/openems/impl/protocol/keba/ReceiveWorker.java b/edge/src/io/openems/impl/protocol/keba/ReceiveWorker.java index db3c1cb3d75..e1b78af0dfe 100644 --- a/edge/src/io/openems/impl/protocol/keba/ReceiveWorker.java +++ b/edge/src/io/openems/impl/protocol/keba/ReceiveWorker.java @@ -9,7 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; public class ReceiveWorker implements Runnable { diff --git a/edge/src/io/openems/impl/protocol/modbus/ModbusDevice.java b/edge/src/io/openems/impl/protocol/modbus/ModbusDevice.java index 9e76ebf17cd..63e48182774 100644 --- a/edge/src/io/openems/impl/protocol/modbus/ModbusDevice.java +++ b/edge/src/io/openems/impl/protocol/modbus/ModbusDevice.java @@ -27,7 +27,7 @@ import io.openems.api.device.Device; import io.openems.api.doc.ChannelInfo; import io.openems.api.exception.ConfigException; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; public abstract class ModbusDevice extends Device { diff --git a/edge/src/io/openems/impl/protocol/simulator/SimulatorDevice.java b/edge/src/io/openems/impl/protocol/simulator/SimulatorDevice.java index 145a350f665..7d5bbc27cd3 100644 --- a/edge/src/io/openems/impl/protocol/simulator/SimulatorDevice.java +++ b/edge/src/io/openems/impl/protocol/simulator/SimulatorDevice.java @@ -23,7 +23,7 @@ import io.openems.api.bridge.Bridge; import io.openems.api.device.Device; import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; @ThingInfo(title = "Simulator") public abstract class SimulatorDevice extends Device { diff --git a/edge/src/io/openems/impl/protocol/studer/StuderBridge.java b/edge/src/io/openems/impl/protocol/studer/StuderBridge.java index 5634aed931c..11ed85cf589 100644 --- a/edge/src/io/openems/impl/protocol/studer/StuderBridge.java +++ b/edge/src/io/openems/impl/protocol/studer/StuderBridge.java @@ -29,7 +29,7 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; import io.openems.impl.protocol.studer.internal.Request; import io.openems.impl.protocol.studer.internal.StuderConnection; import io.openems.impl.protocol.studer.internal.request.ReadRequest; diff --git a/edge/src/io/openems/impl/protocol/studer/StuderDevice.java b/edge/src/io/openems/impl/protocol/studer/StuderDevice.java index 57e89bf994f..f5d4e7d2294 100644 --- a/edge/src/io/openems/impl/protocol/studer/StuderDevice.java +++ b/edge/src/io/openems/impl/protocol/studer/StuderDevice.java @@ -25,7 +25,7 @@ import io.openems.api.device.Device; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; @ThingInfo(title = "Studer") public abstract class StuderDevice extends Device { diff --git a/edge/src/io/openems/impl/protocol/studer/StuderDeviceNature.java b/edge/src/io/openems/impl/protocol/studer/StuderDeviceNature.java index 4f2a73a711c..a841562a2b2 100644 --- a/edge/src/io/openems/impl/protocol/studer/StuderDeviceNature.java +++ b/edge/src/io/openems/impl/protocol/studer/StuderDeviceNature.java @@ -39,7 +39,7 @@ import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; import io.openems.api.thing.ThingChannelsUpdatedListener; import io.openems.impl.protocol.studer.internal.StuderProtocol; import io.openems.impl.protocol.studer.internal.property.ReadProperty; diff --git a/edge/src/io/openems/impl/protocol/studer/internal/property/ReadProperty.java b/edge/src/io/openems/impl/protocol/studer/internal/property/ReadProperty.java index c243bbce80f..04d8b9492e9 100644 --- a/edge/src/io/openems/impl/protocol/studer/internal/property/ReadProperty.java +++ b/edge/src/io/openems/impl/protocol/studer/internal/property/ReadProperty.java @@ -1,36 +1,36 @@ -package io.openems.impl.protocol.studer.internal.property; - -import java.io.IOException; - -import io.openems.api.exception.OpenemsException; -import io.openems.impl.protocol.studer.StuderBridge; -import io.openems.impl.protocol.studer.StuderChannel; -import io.openems.impl.protocol.studer.internal.request.ReadRequest; -import io.openems.impl.protocol.studer.internal.request.ReadResponse; - -public interface ReadProperty extends StuderProperty { - - public default void updateValue(int srcAddress, int dstAddress, StuderBridge studerBridge) throws OpenemsException { - try { - ReadRequest readRequest = readRequest(srcAddress, dstAddress); - studerBridge.execute(readRequest); - ReadResponse response = readRequest.getResponse(); - T value = response.getValue(); - StuderChannel channel = channel(); - if (channel == null) { - return; - } else if (channel instanceof StuderReadChannel) { - ((StuderReadChannel) channel).updateValue(value); - } else if (channel instanceof StuderWriteChannel) { - ((StuderWriteChannel) channel).updateValue(value); - } else { - throw new OpenemsException("Unable to set value [" + value + "]. Channel [" + channel.address() - + "] is no StuderReadChannel or StuderWriteChannel."); - } - } catch (IOException e) { - throw new OpenemsException("Unable to update value", e); - } - } - - public ReadRequest readRequest(int srcAddress, int dstAddress); -} +package io.openems.impl.protocol.studer.internal.property; + +import java.io.IOException; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.impl.protocol.studer.StuderBridge; +import io.openems.impl.protocol.studer.StuderChannel; +import io.openems.impl.protocol.studer.internal.request.ReadRequest; +import io.openems.impl.protocol.studer.internal.request.ReadResponse; + +public interface ReadProperty extends StuderProperty { + + public default void updateValue(int srcAddress, int dstAddress, StuderBridge studerBridge) throws OpenemsException { + try { + ReadRequest readRequest = readRequest(srcAddress, dstAddress); + studerBridge.execute(readRequest); + ReadResponse response = readRequest.getResponse(); + T value = response.getValue(); + StuderChannel channel = channel(); + if (channel == null) { + return; + } else if (channel instanceof StuderReadChannel) { + ((StuderReadChannel) channel).updateValue(value); + } else if (channel instanceof StuderWriteChannel) { + ((StuderWriteChannel) channel).updateValue(value); + } else { + throw new OpenemsException("Unable to set value [" + value + "]. Channel [" + channel.address() + + "] is no StuderReadChannel or StuderWriteChannel."); + } + } catch (IOException e) { + throw new OpenemsException("Unable to update value", e); + } + } + + public ReadRequest readRequest(int srcAddress, int dstAddress); +} diff --git a/edge/src/io/openems/impl/protocol/studer/internal/property/WriteProperty.java b/edge/src/io/openems/impl/protocol/studer/internal/property/WriteProperty.java index d9ad179e227..fe28a6dbf21 100644 --- a/edge/src/io/openems/impl/protocol/studer/internal/property/WriteProperty.java +++ b/edge/src/io/openems/impl/protocol/studer/internal/property/WriteProperty.java @@ -1,34 +1,34 @@ -package io.openems.impl.protocol.studer.internal.property; - -import java.io.IOException; -import java.util.Optional; - -import io.openems.api.exception.OpenemsException; -import io.openems.impl.protocol.studer.StuderBridge; -import io.openems.impl.protocol.studer.internal.request.WriteRequest; - -public interface WriteProperty extends ReadProperty { - - @Override - public StuderWriteChannel channel(); - - public WriteRequest writeRequest(int srcAddress, int dstAddress, T value); - - public default void writeValue(int srcAddress, int dstAddress, StuderBridge studerBridge) throws OpenemsException { - try { - T value; - Optional valueOptional = this.channel().writeShadowCopy(); - if (valueOptional.isPresent()) { - value = valueOptional.get(); - } else { - return; - } - System.out.println("Write value[" + value + "] to " + this.channel().address()); - WriteRequest writeRequest = writeRequest(srcAddress, dstAddress, value); - studerBridge.execute(writeRequest); - } catch (IOException e) { - throw new OpenemsException("Unable to write value", e); - } - } - -} +package io.openems.impl.protocol.studer.internal.property; + +import java.io.IOException; +import java.util.Optional; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.impl.protocol.studer.StuderBridge; +import io.openems.impl.protocol.studer.internal.request.WriteRequest; + +public interface WriteProperty extends ReadProperty { + + @Override + public StuderWriteChannel channel(); + + public WriteRequest writeRequest(int srcAddress, int dstAddress, T value); + + public default void writeValue(int srcAddress, int dstAddress, StuderBridge studerBridge) throws OpenemsException { + try { + T value; + Optional valueOptional = this.channel().writeShadowCopy(); + if (valueOptional.isPresent()) { + value = valueOptional.get(); + } else { + return; + } + System.out.println("Write value[" + value + "] to " + this.channel().address()); + WriteRequest writeRequest = writeRequest(srcAddress, dstAddress, value); + studerBridge.execute(writeRequest); + } catch (IOException e) { + throw new OpenemsException("Unable to write value", e); + } + } + +} diff --git a/edge/src/io/openems/impl/protocol/system/SystemDevice.java b/edge/src/io/openems/impl/protocol/system/SystemDevice.java index b9a17a9e557..2bdf9ac7291 100644 --- a/edge/src/io/openems/impl/protocol/system/SystemDevice.java +++ b/edge/src/io/openems/impl/protocol/system/SystemDevice.java @@ -23,7 +23,7 @@ import io.openems.api.bridge.Bridge; import io.openems.api.device.Device; import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.OpenemsException; +import io.openems.common.exceptions.OpenemsException; @ThingInfo(title = "Operating system") public abstract class SystemDevice extends Device { diff --git a/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java b/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java index 761e9fedc44..8c72347bf84 100644 --- a/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java +++ b/edge/src/io/openems/impl/scheduler/time/WeekTimeScheduler.java @@ -43,10 +43,10 @@ import io.openems.api.doc.ThingInfo; import io.openems.api.exception.ConfigException; import io.openems.api.exception.InvalidValueException; -import io.openems.api.exception.ReflectionException; import io.openems.api.scheduler.Scheduler; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.JsonUtils; import io.openems.core.ThingRepository; -import io.openems.core.utilities.JsonUtils; @ThingInfo(title = "Weekly App-Planner", description = "Define recurring weekly plans.") public class WeekTimeScheduler extends Scheduler { @@ -133,7 +133,7 @@ protected void execute() { for (Bridge bridge : thingRepository.getBridges()) { bridge.triggerWrite(); } - } catch (InvalidValueException | DateTimeParseException | ConfigException | ReflectionException e) { + } catch (DateTimeParseException | OpenemsException e) { log.error(e.getMessage()); } } @@ -148,7 +148,7 @@ private List getAlwaysController() { return controller; } - private List getActiveControllers() throws InvalidValueException, ConfigException, ReflectionException { + private List getActiveControllers() throws OpenemsException { JsonArray jHours = getJsonOfDay(LocalDate.now().getDayOfWeek()); LocalTime time = LocalTime.now(); List controllers = new ArrayList<>(); @@ -185,8 +185,7 @@ private JsonArray getJsonOfDay(DayOfWeek day) throws InvalidValueException { } } - private List floorController(JsonArray jHours, LocalTime time) - throws ConfigException, ReflectionException { + private List floorController(JsonArray jHours, LocalTime time) throws OpenemsException { // fill times map; sorted by hour TreeMap times = new TreeMap<>(); for (JsonElement jHourElement : jHours) { diff --git a/edge/src/io/openems/test/controller/asymmetric/phaserectification/PhaseRectificationActivePowerTest.java b/edge/src/io/openems/test/controller/asymmetric/phaserectification/PhaseRectificationActivePowerTest.java index 0462e1b0798..3728b990a0f 100644 --- a/edge/src/io/openems/test/controller/asymmetric/phaserectification/PhaseRectificationActivePowerTest.java +++ b/edge/src/io/openems/test/controller/asymmetric/phaserectification/PhaseRectificationActivePowerTest.java @@ -1,86 +1,86 @@ -package io.openems.test.controller.asymmetric.phaserectification; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.openems.api.exception.WriteChannelException; -import io.openems.impl.controller.asymmetric.phaserectification.Ess; -import io.openems.impl.controller.asymmetric.phaserectification.Meter; -import io.openems.impl.controller.asymmetric.phaserectification.PhaseRectificationActivePowerController; -import io.openems.test.utils.devicenatures.UnitTestAsymmetricEssNature; -import io.openems.test.utils.devicenatures.UnitTestAsymmetricMeterNature; - -public class PhaseRectificationActivePowerTest { - - private static PhaseRectificationActivePowerController controller; - private static UnitTestAsymmetricEssNature ess; - private static UnitTestAsymmetricMeterNature meter; - private static Ess essThingMap; - private static Meter meterThingMap; - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - ess = new UnitTestAsymmetricEssNature("ess0"); - meter = new UnitTestAsymmetricMeterNature("meter0"); - controller = new PhaseRectificationActivePowerController(); - essThingMap = new Ess(ess); - meterThingMap = new Meter(meter); - controller.ess.updateValue(essThingMap, true); - controller.meter.updateValue(meterThingMap, true); - } - - @Before - public void beforeTest() { - ess.setActivePowerL1.shadowCopyAndReset(); - ess.setActivePowerL2.shadowCopyAndReset(); - ess.setActivePowerL3.shadowCopyAndReset(); - ess.setReactivePowerL1.shadowCopyAndReset(); - ess.setReactivePowerL2.shadowCopyAndReset(); - ess.setReactivePowerL3.shadowCopyAndReset(); - ess.setWorkState.shadowCopyAndReset(); - ess.activePowerL1.setValue(0L); - ess.activePowerL2.setValue(0L); - ess.activePowerL3.setValue(0L); - meter.activePowerL1.setValue(0L); - meter.activePowerL2.setValue(0L); - meter.activePowerL3.setValue(0L); - ess.soc.setValue(35L); - ess.minSoc.setValue(15); - ess.chargeSoc.setValue(10); - ess.allowedApparent.setValue(100000L); - ess.allowedCharge.setValue(0L); - ess.allowedDischarge.setValue(0L); - } - - @Test - public void test1() { - ess.activePowerL1.setValue(1000L); - ess.activePowerL2.setValue(100L); - ess.activePowerL3.setValue(750L); - meter.activePowerL1.setValue(-500L); - meter.activePowerL2.setValue(-500L); - meter.activePowerL3.setValue(-500L); - try { - ess.setActivePowerL1.pushWriteMax(33333L); - ess.setActivePowerL1.pushWriteMin(-33333L); - ess.setActivePowerL2.pushWriteMax(33333L); - ess.setActivePowerL2.pushWriteMin(-33333L); - ess.setActivePowerL3.pushWriteMax(33333L); - ess.setActivePowerL3.pushWriteMin(-33333L); - controller.run(); - long setActivePowerL1 = ess.setActivePowerL1.getWriteValue().get(); - long setActivePowerL2 = ess.setActivePowerL2.getWriteValue().get(); - long setActivePowerL3 = ess.setActivePowerL3.getWriteValue().get(); - assertEquals(384L, setActivePowerL1); - assertEquals(-516L, setActivePowerL2); - assertEquals(134L, setActivePowerL3); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - -} +package io.openems.test.controller.asymmetric.phaserectification; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.openems.api.exception.WriteChannelException; +import io.openems.impl.controller.asymmetric.phaserectification.Ess; +import io.openems.impl.controller.asymmetric.phaserectification.Meter; +import io.openems.impl.controller.asymmetric.phaserectification.PhaseRectificationActivePowerController; +import io.openems.test.utils.devicenatures.UnitTestAsymmetricEssNature; +import io.openems.test.utils.devicenatures.UnitTestAsymmetricMeterNature; + +public class PhaseRectificationActivePowerTest { + + private static PhaseRectificationActivePowerController controller; + private static UnitTestAsymmetricEssNature ess; + private static UnitTestAsymmetricMeterNature meter; + private static Ess essThingMap; + private static Meter meterThingMap; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + ess = new UnitTestAsymmetricEssNature("ess0"); + meter = new UnitTestAsymmetricMeterNature("meter0"); + controller = new PhaseRectificationActivePowerController(); + essThingMap = new Ess(ess); + meterThingMap = new Meter(meter); + controller.ess.updateValue(essThingMap, true); + controller.meter.updateValue(meterThingMap, true); + } + + @Before + public void beforeTest() { + ess.setActivePowerL1.shadowCopyAndReset(); + ess.setActivePowerL2.shadowCopyAndReset(); + ess.setActivePowerL3.shadowCopyAndReset(); + ess.setReactivePowerL1.shadowCopyAndReset(); + ess.setReactivePowerL2.shadowCopyAndReset(); + ess.setReactivePowerL3.shadowCopyAndReset(); + ess.setWorkState.shadowCopyAndReset(); + ess.activePowerL1.setValue(0L); + ess.activePowerL2.setValue(0L); + ess.activePowerL3.setValue(0L); + meter.activePowerL1.setValue(0L); + meter.activePowerL2.setValue(0L); + meter.activePowerL3.setValue(0L); + ess.soc.setValue(35L); + ess.minSoc.setValue(15); + ess.chargeSoc.setValue(10); + ess.allowedApparent.setValue(100000L); + ess.allowedCharge.setValue(0L); + ess.allowedDischarge.setValue(0L); + } + + @Test + public void test1() { + ess.activePowerL1.setValue(1000L); + ess.activePowerL2.setValue(100L); + ess.activePowerL3.setValue(750L); + meter.activePowerL1.setValue(-500L); + meter.activePowerL2.setValue(-500L); + meter.activePowerL3.setValue(-500L); + try { + ess.setActivePowerL1.pushWriteMax(33333L); + ess.setActivePowerL1.pushWriteMin(-33333L); + ess.setActivePowerL2.pushWriteMax(33333L); + ess.setActivePowerL2.pushWriteMin(-33333L); + ess.setActivePowerL3.pushWriteMax(33333L); + ess.setActivePowerL3.pushWriteMin(-33333L); + controller.run(); + long setActivePowerL1 = ess.setActivePowerL1.getWriteValue().get(); + long setActivePowerL2 = ess.setActivePowerL2.getWriteValue().get(); + long setActivePowerL3 = ess.setActivePowerL3.getWriteValue().get(); + assertEquals(384L, setActivePowerL1); + assertEquals(-516L, setActivePowerL2); + assertEquals(134L, setActivePowerL3); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + +} diff --git a/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java b/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java index 40eeb88ac4d..38db17dd3af 100644 --- a/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java +++ b/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java @@ -1,226 +1,226 @@ -package io.openems.test.controller.supplybusswitch; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.HashMap; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; - -import io.openems.api.channel.WriteChannel; -import io.openems.api.exception.InvalidValueException; -import io.openems.impl.controller.supplybusswitch.Ess; -import io.openems.impl.controller.supplybusswitch.Supplybus; -import io.openems.test.utils.channel.UnitTestWriteChannel; -import io.openems.test.utils.devicenatures.UnitTestSymmetricEssNature; - -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class SupplyBusTest { - - private static Supplybus sb; - private static UnitTestSymmetricEssNature ess1; - private static UnitTestSymmetricEssNature ess2; - private static UnitTestSymmetricEssNature ess3; - private static UnitTestSymmetricEssNature ess4; - private static Ess essMap1; - private static Ess essMap2; - private static Ess essMap3; - private static Ess essMap4; - private static UnitTestWriteChannel output1; - private static UnitTestWriteChannel output2; - private static UnitTestWriteChannel output3; - private static UnitTestWriteChannel output4; - private static UnitTestWriteChannel sbOnIndication; - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - HashMap> essSet = new HashMap<>(); - ess1 = new UnitTestSymmetricEssNature("ess0"); - essMap1 = new Ess(ess1); - output1 = new UnitTestWriteChannel<>("output", "0"); - essSet.put(essMap1, output1); - ess2 = new UnitTestSymmetricEssNature("ess1"); - essMap2 = new Ess(ess2); - output2 = new UnitTestWriteChannel<>("output", "1"); - essSet.put(essMap2, output2); - ess3 = new UnitTestSymmetricEssNature("ess2"); - essMap3 = new Ess(ess3); - output3 = new UnitTestWriteChannel<>("output", "2"); - essSet.put(essMap3, output3); - ess4 = new UnitTestSymmetricEssNature("ess3"); - essMap4 = new Ess(ess4); - output4 = new UnitTestWriteChannel<>("output", "3"); - essSet.put(essMap4, output4); - sbOnIndication = new UnitTestWriteChannel<>("custom", "sb1On"); - sb = new Supplybus(essSet, "sb1", essMap1, 1000L, sbOnIndication, new ArrayList<>()); - } - - @Before - public void beforeTest() { - ess1.setActivePower.shadowCopyAndReset(); - ess1.setReactivePower.shadowCopyAndReset(); - ess1.setWorkState.shadowCopyAndReset(); - ess1.activePower.setValue(0L); - ess1.soc.setValue(35L); - ess1.minSoc.setValue(15); - ess1.chargeSoc.setValue(10); - ess1.allowedApparent.setValue(40000L); - ess1.allowedCharge.setValue(-40000L); - ess1.allowedDischarge.setValue(40000L); - ess1.gridMode.setValue(0L); - ess2.setActivePower.shadowCopyAndReset(); - ess2.setReactivePower.shadowCopyAndReset(); - ess2.setWorkState.shadowCopyAndReset(); - ess2.activePower.setValue(0L); - ess2.soc.setValue(35L); - ess2.minSoc.setValue(15); - ess2.chargeSoc.setValue(10); - ess2.allowedApparent.setValue(40000L); - ess2.allowedCharge.setValue(-40000L); - ess2.allowedDischarge.setValue(40000L); - ess2.gridMode.setValue(0L); - ess3.setActivePower.shadowCopyAndReset(); - ess3.setReactivePower.shadowCopyAndReset(); - ess3.setWorkState.shadowCopyAndReset(); - ess3.activePower.setValue(0L); - ess3.soc.setValue(35L); - ess3.minSoc.setValue(15); - ess3.chargeSoc.setValue(10); - ess3.allowedApparent.setValue(40000L); - ess3.allowedCharge.setValue(-40000L); - ess3.allowedDischarge.setValue(40000L); - ess3.gridMode.setValue(0L); - ess4.setActivePower.shadowCopyAndReset(); - ess4.setReactivePower.shadowCopyAndReset(); - ess4.setWorkState.shadowCopyAndReset(); - ess4.activePower.setValue(0L); - ess4.soc.setValue(35L); - ess4.minSoc.setValue(15); - ess4.chargeSoc.setValue(10); - ess4.allowedApparent.setValue(40000L); - ess4.allowedCharge.setValue(-40000L); - ess4.allowedDischarge.setValue(40000L); - ess4.gridMode.setValue(0L); - } - - @Test - public void test1() { - // start and connect with most charged ess - ess3.soc.setValue(49L); - ess1.soc.setValue(60L); - output1.setValue(false); - output2.setValue(false); - output3.setValue(false); - output4.setValue(false); - // Unknown - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Disconnecting - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Disconnected - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - assertEquals(sbOnIndication.getWriteValue().isPresent(), true); - assertEquals((long) sbOnIndication.getWriteValue().get(), 0); - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Connecting 1 - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - // output not set switchdelay not expired - assertEquals(output3.getWriteValue().isPresent(), false); - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Sleep until switchdelay expired - try { - Thread.sleep(1000L); - } catch (InterruptedException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Connecting 2 - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - assertEquals(output3.getWriteValue().isPresent(), true); - assertEquals(output3.getWriteValue().get(), true); - output3.setValue(true); - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Connecting 3 - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - // Connected - try { - sb.run(); - } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - assertEquals(sbOnIndication.getWriteValue().isPresent(), true); - assertEquals((long) sbOnIndication.getWriteValue().get(), 1L); - output1.shadowCopyAndReset(); - output2.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - output3.shadowCopyAndReset(); - sbOnIndication.shadowCopyAndReset(); - } - -} +package io.openems.test.controller.supplybusswitch; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import io.openems.api.channel.WriteChannel; +import io.openems.api.exception.InvalidValueException; +import io.openems.impl.controller.supplybusswitch.Ess; +import io.openems.impl.controller.supplybusswitch.Supplybus; +import io.openems.test.utils.channel.UnitTestWriteChannel; +import io.openems.test.utils.devicenatures.UnitTestSymmetricEssNature; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SupplyBusTest { + + private static Supplybus sb; + private static UnitTestSymmetricEssNature ess1; + private static UnitTestSymmetricEssNature ess2; + private static UnitTestSymmetricEssNature ess3; + private static UnitTestSymmetricEssNature ess4; + private static Ess essMap1; + private static Ess essMap2; + private static Ess essMap3; + private static Ess essMap4; + private static UnitTestWriteChannel output1; + private static UnitTestWriteChannel output2; + private static UnitTestWriteChannel output3; + private static UnitTestWriteChannel output4; + private static UnitTestWriteChannel sbOnIndication; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + HashMap> essSet = new HashMap<>(); + ess1 = new UnitTestSymmetricEssNature("ess0"); + essMap1 = new Ess(ess1); + output1 = new UnitTestWriteChannel<>("output", "0"); + essSet.put(essMap1, output1); + ess2 = new UnitTestSymmetricEssNature("ess1"); + essMap2 = new Ess(ess2); + output2 = new UnitTestWriteChannel<>("output", "1"); + essSet.put(essMap2, output2); + ess3 = new UnitTestSymmetricEssNature("ess2"); + essMap3 = new Ess(ess3); + output3 = new UnitTestWriteChannel<>("output", "2"); + essSet.put(essMap3, output3); + ess4 = new UnitTestSymmetricEssNature("ess3"); + essMap4 = new Ess(ess4); + output4 = new UnitTestWriteChannel<>("output", "3"); + essSet.put(essMap4, output4); + sbOnIndication = new UnitTestWriteChannel<>("custom", "sb1On"); + sb = new Supplybus(essSet, "sb1", essMap1, 1000L, sbOnIndication, new ArrayList<>()); + } + + @Before + public void beforeTest() { + ess1.setActivePower.shadowCopyAndReset(); + ess1.setReactivePower.shadowCopyAndReset(); + ess1.setWorkState.shadowCopyAndReset(); + ess1.activePower.setValue(0L); + ess1.soc.setValue(35L); + ess1.minSoc.setValue(15); + ess1.chargeSoc.setValue(10); + ess1.allowedApparent.setValue(40000L); + ess1.allowedCharge.setValue(-40000L); + ess1.allowedDischarge.setValue(40000L); + ess1.gridMode.setValue(0L); + ess2.setActivePower.shadowCopyAndReset(); + ess2.setReactivePower.shadowCopyAndReset(); + ess2.setWorkState.shadowCopyAndReset(); + ess2.activePower.setValue(0L); + ess2.soc.setValue(35L); + ess2.minSoc.setValue(15); + ess2.chargeSoc.setValue(10); + ess2.allowedApparent.setValue(40000L); + ess2.allowedCharge.setValue(-40000L); + ess2.allowedDischarge.setValue(40000L); + ess2.gridMode.setValue(0L); + ess3.setActivePower.shadowCopyAndReset(); + ess3.setReactivePower.shadowCopyAndReset(); + ess3.setWorkState.shadowCopyAndReset(); + ess3.activePower.setValue(0L); + ess3.soc.setValue(35L); + ess3.minSoc.setValue(15); + ess3.chargeSoc.setValue(10); + ess3.allowedApparent.setValue(40000L); + ess3.allowedCharge.setValue(-40000L); + ess3.allowedDischarge.setValue(40000L); + ess3.gridMode.setValue(0L); + ess4.setActivePower.shadowCopyAndReset(); + ess4.setReactivePower.shadowCopyAndReset(); + ess4.setWorkState.shadowCopyAndReset(); + ess4.activePower.setValue(0L); + ess4.soc.setValue(35L); + ess4.minSoc.setValue(15); + ess4.chargeSoc.setValue(10); + ess4.allowedApparent.setValue(40000L); + ess4.allowedCharge.setValue(-40000L); + ess4.allowedDischarge.setValue(40000L); + ess4.gridMode.setValue(0L); + } + + @Test + public void test1() { + // start and connect with most charged ess + ess3.soc.setValue(49L); + ess1.soc.setValue(60L); + output1.setValue(false); + output2.setValue(false); + output3.setValue(false); + output4.setValue(false); + // Unknown + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Disconnecting + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Disconnected + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + assertEquals(sbOnIndication.getWriteValue().isPresent(), true); + assertEquals((long) sbOnIndication.getWriteValue().get(), 0); + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Connecting 1 + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // output not set switchdelay not expired + assertEquals(output3.getWriteValue().isPresent(), false); + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Sleep until switchdelay expired + try { + Thread.sleep(1000L); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Connecting 2 + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + assertEquals(output3.getWriteValue().isPresent(), true); + assertEquals(output3.getWriteValue().get(), true); + output3.setValue(true); + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Connecting 3 + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + // Connected + try { + sb.run(); + } catch (InvalidValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + assertEquals(sbOnIndication.getWriteValue().isPresent(), true); + assertEquals((long) sbOnIndication.getWriteValue().get(), 1L); + output1.shadowCopyAndReset(); + output2.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + output3.shadowCopyAndReset(); + sbOnIndication.shadowCopyAndReset(); + } + +} diff --git a/edge/src/io/openems/test/controller/symmetric/balancing/BalancingTest.java b/edge/src/io/openems/test/controller/symmetric/balancing/BalancingTest.java index 797ef48892c..6a145fed508 100644 --- a/edge/src/io/openems/test/controller/symmetric/balancing/BalancingTest.java +++ b/edge/src/io/openems/test/controller/symmetric/balancing/BalancingTest.java @@ -1,170 +1,170 @@ -package io.openems.test.controller.symmetric.balancing; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.openems.api.exception.WriteChannelException; -import io.openems.impl.controller.symmetric.balancing.BalancingController; -import io.openems.impl.controller.symmetric.balancing.Ess; -import io.openems.impl.controller.symmetric.balancing.Meter; -import io.openems.test.utils.devicenatures.UnitTestSymmetricEssNature; -import io.openems.test.utils.devicenatures.UnitTestSymmetricMeterNature; - -public class BalancingTest { - - private static BalancingController controller; - private static UnitTestSymmetricEssNature ess; - private static UnitTestSymmetricMeterNature meter; - private static Ess essThingMap; - private static Meter meterThingMap; - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - ess = new UnitTestSymmetricEssNature("ess0"); - meter = new UnitTestSymmetricMeterNature("meter0"); - controller = new BalancingController(); - essThingMap = new Ess(ess); - meterThingMap = new Meter(meter); - List essSet = new ArrayList(); - essSet.add(essThingMap); - controller.esss.updateValue(essSet, true); - controller.meter.updateValue(meterThingMap, true); - } - - @Before - public void beforeTest() { - ess.setActivePower.shadowCopyAndReset(); - ess.setReactivePower.shadowCopyAndReset(); - ess.setWorkState.shadowCopyAndReset(); - ess.activePower.setValue(0L); - meter.activePower.setValue(0L); - ess.soc.setValue(35L); - ess.minSoc.setValue(15); - ess.chargeSoc.setValue(10); - ess.allowedApparent.setValue(40000L); - ess.allowedCharge.setValue(-40000L); - ess.allowedDischarge.setValue(40000L); - } - - @Test - public void powerCalculationWithoutLimitations() { - ess.activePower.setValue(1000L); - meter.activePower.setValue(-500L); - try { - ess.setActivePower.pushWriteMax(40000L); - - ess.setActivePower.pushWriteMin(-40000L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(400L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - - @Test - public void powerCalculationWithDischargeLimit() { - ess.activePower.setValue(1000L); - meter.activePower.setValue(-500L); - ess.allowedDischarge.setValue(300L); - try { - ess.setActivePower.pushWriteMax(40000L); - - ess.setActivePower.pushWriteMin(-40000L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(300L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - - @Test - public void powerCalculationWithChargeLimit() { - ess.activePower.setValue(100L); - meter.activePower.setValue(-500L); - ess.allowedCharge.setValue(-100L); - try { - ess.setActivePower.pushWriteMax(40000L); - - ess.setActivePower.pushWriteMin(-40000L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(-100L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - - @Test - public void powerCalculationWithPositiveSetActivePowerMaxLimit() { - ess.activePower.setValue(1000L); - meter.activePower.setValue(-500L); - try { - ess.setActivePower.pushWriteMax(300L); - - ess.setActivePower.pushWriteMin(-40000L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(300L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - - @Test - public void powerCalculationWithNegativeSetActivePowerMaxLimit() { - ess.activePower.setValue(1000L); - meter.activePower.setValue(-500L); - try { - ess.setActivePower.pushWriteMax(-300L); - - ess.setActivePower.pushWriteMin(-40000L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(-300L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - - @Test - public void powerCalculationWithPositiveSetActivePowerMinLimit() { - ess.activePower.setValue(100L); - meter.activePower.setValue(-500L); - try { - ess.setActivePower.pushWriteMax(40000L); - - ess.setActivePower.pushWriteMin(400L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(400L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - - @Test - public void powerCalculationWithNegativeSetActivePowerMinLimit() { - ess.activePower.setValue(100L); - meter.activePower.setValue(-500L); - try { - ess.setActivePower.pushWriteMax(40000L); - - ess.setActivePower.pushWriteMin(-400L); - controller.run(); - long setActivePower = ess.setActivePower.getWriteValue().get(); - assertEquals(-400L, setActivePower); - } catch (WriteChannelException e) { - fail("unexpected WriteChannelException" + e.getMessage()); - } - } - -} +package io.openems.test.controller.symmetric.balancing; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.openems.api.exception.WriteChannelException; +import io.openems.impl.controller.symmetric.balancing.BalancingController; +import io.openems.impl.controller.symmetric.balancing.Ess; +import io.openems.impl.controller.symmetric.balancing.Meter; +import io.openems.test.utils.devicenatures.UnitTestSymmetricEssNature; +import io.openems.test.utils.devicenatures.UnitTestSymmetricMeterNature; + +public class BalancingTest { + + private static BalancingController controller; + private static UnitTestSymmetricEssNature ess; + private static UnitTestSymmetricMeterNature meter; + private static Ess essThingMap; + private static Meter meterThingMap; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + ess = new UnitTestSymmetricEssNature("ess0"); + meter = new UnitTestSymmetricMeterNature("meter0"); + controller = new BalancingController(); + essThingMap = new Ess(ess); + meterThingMap = new Meter(meter); + List essSet = new ArrayList(); + essSet.add(essThingMap); + controller.esss.updateValue(essSet, true); + controller.meter.updateValue(meterThingMap, true); + } + + @Before + public void beforeTest() { + ess.setActivePower.shadowCopyAndReset(); + ess.setReactivePower.shadowCopyAndReset(); + ess.setWorkState.shadowCopyAndReset(); + ess.activePower.setValue(0L); + meter.activePower.setValue(0L); + ess.soc.setValue(35L); + ess.minSoc.setValue(15); + ess.chargeSoc.setValue(10); + ess.allowedApparent.setValue(40000L); + ess.allowedCharge.setValue(-40000L); + ess.allowedDischarge.setValue(40000L); + } + + @Test + public void powerCalculationWithoutLimitations() { + ess.activePower.setValue(1000L); + meter.activePower.setValue(-500L); + try { + ess.setActivePower.pushWriteMax(40000L); + + ess.setActivePower.pushWriteMin(-40000L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(400L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + + @Test + public void powerCalculationWithDischargeLimit() { + ess.activePower.setValue(1000L); + meter.activePower.setValue(-500L); + ess.allowedDischarge.setValue(300L); + try { + ess.setActivePower.pushWriteMax(40000L); + + ess.setActivePower.pushWriteMin(-40000L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(300L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + + @Test + public void powerCalculationWithChargeLimit() { + ess.activePower.setValue(100L); + meter.activePower.setValue(-500L); + ess.allowedCharge.setValue(-100L); + try { + ess.setActivePower.pushWriteMax(40000L); + + ess.setActivePower.pushWriteMin(-40000L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(-100L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + + @Test + public void powerCalculationWithPositiveSetActivePowerMaxLimit() { + ess.activePower.setValue(1000L); + meter.activePower.setValue(-500L); + try { + ess.setActivePower.pushWriteMax(300L); + + ess.setActivePower.pushWriteMin(-40000L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(300L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + + @Test + public void powerCalculationWithNegativeSetActivePowerMaxLimit() { + ess.activePower.setValue(1000L); + meter.activePower.setValue(-500L); + try { + ess.setActivePower.pushWriteMax(-300L); + + ess.setActivePower.pushWriteMin(-40000L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(-300L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + + @Test + public void powerCalculationWithPositiveSetActivePowerMinLimit() { + ess.activePower.setValue(100L); + meter.activePower.setValue(-500L); + try { + ess.setActivePower.pushWriteMax(40000L); + + ess.setActivePower.pushWriteMin(400L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(400L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + + @Test + public void powerCalculationWithNegativeSetActivePowerMinLimit() { + ess.activePower.setValue(100L); + meter.activePower.setValue(-500L); + try { + ess.setActivePower.pushWriteMax(40000L); + + ess.setActivePower.pushWriteMin(-400L); + controller.run(); + long setActivePower = ess.setActivePower.getWriteValue().get(); + assertEquals(-400L, setActivePower); + } catch (WriteChannelException e) { + fail("unexpected WriteChannelException" + e.getMessage()); + } + } + +} 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 f22614e80d5..72329ea6afc 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -26,6 +26,33 @@ // TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions public class JsonUtils { + public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isBoolean()) { + throw new OpenemsException("This is not a Boolean: " + jPrimitive); + } + return jPrimitive.getAsBoolean(); + }; + + public static boolean getAsBoolean(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (!jPrimitive.isBoolean()) { + throw new OpenemsException("Element [" + memberName + "] is not a Boolean: " + jPrimitive); + } + return jPrimitive.getAsBoolean(); + }; + + public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsInt(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Integer.parseInt(string); + } + throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); + } + public static JsonArray getAsJsonArray(JsonElement jElement) throws OpenemsException { if (!jElement.isJsonArray()) { throw new OpenemsException("This is not a JsonArray: " + jElement); @@ -40,72 +67,129 @@ public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) } return jSubElement.getAsJsonArray(); }; - - public static Optional getAsOptionalJsonArray(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsJsonArray(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); - } - } + public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { + // null + if (value == null) { + return null; + } + // optional + if (value instanceof Optional) { + if (!((Optional) value).isPresent()) { + return null; + } else { + value = ((Optional) value).get(); + } + } + if (value instanceof Number) { + /* + * Number + */ + return new JsonPrimitive((Number) value); + } else if(value instanceof ChannelEnum) { + /* + * ChannelEnum + */ + return new JsonPrimitive(((ChannelEnum)value).getValue()); + } else if (value instanceof String) { + /* + * String + */ + return new JsonPrimitive((String) value); + } else if (value instanceof Boolean) { + /* + * Boolean + */ + return new JsonPrimitive((Boolean) value); + } else if (value instanceof Inet4Address) { + /* + * Inet4Address + */ + return new JsonPrimitive(((Inet4Address) value).getHostAddress()); + } else if (value instanceof JsonElement) { + /* + * JsonElement + */ + return (JsonElement) value; + } else if (value instanceof Long[]){ + /* + * Long-Array + */ + JsonArray js = new JsonArray(); + for (Long l : (Long[]) value){ + js.add(new JsonPrimitive((Long) l)); + } + return js; + } + throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // + + value.getClass().getSimpleName() + "]" // + + " to JSON is not implemented."); + }; + public static JsonObject getAsJsonObject(JsonElement jElement) throws OpenemsException { if (!jElement.isJsonObject()) { throw new OpenemsException("This is not a JsonObject: " + jElement); } return jElement.getAsJsonObject(); - }; + } - public static Optional getAsOptionalJsonObject(JsonElement jElement) { - try { - return Optional.of(getAsJsonObject(jElement)); - } catch (OpenemsException e) { - return Optional.empty(); - } - }; - public static JsonObject getAsJsonObject(JsonElement jElement, String memberName) throws OpenemsException { JsonElement jsubElement = getSubElement(jElement, memberName); if (!jsubElement.isJsonObject()) { throw new OpenemsException("Element [" + memberName + "] is not a JsonObject: " + jsubElement); } return jsubElement.getAsJsonObject(); - }; + } - public static Optional getAsOptionalJsonObject(JsonElement jElement, String memberName) { + public static long getAsLong(JsonElement jElement, String memberName) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); + if (jPrimitive.isNumber()) { + return jPrimitive.getAsLong(); + } else if (jPrimitive.isString()) { + String string = jPrimitive.getAsString(); + return Long.parseLong(string); + } + throw new OpenemsException("[" + memberName + "] is not a Number: " + jPrimitive); + } + + public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { try { - return Optional.of(getAsJsonObject(jElement, memberName)); + return Optional.of(getAsInt(jElement, memberName)); } catch (OpenemsException e) { return Optional.empty(); } } - public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws OpenemsException { - JsonElement jSubElement = getSubElement(jElement, memberName); - return getAsPrimitive(jSubElement); + public static Optional getAsOptionalJsonArray(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsJsonArray(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); + } } - public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws OpenemsException { - if (!jElement.isJsonPrimitive()) { - throw new OpenemsException("This is not a JsonPrimitive: " + jElement); + public static Optional getAsOptionalJsonObject(JsonElement jElement) { + try { + return Optional.of(getAsJsonObject(jElement)); + } catch (OpenemsException e) { + return Optional.empty(); } - return jElement.getAsJsonPrimitive(); } - public static String getAsString(JsonElement jElement) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isString()) { - throw new OpenemsException("This is not a String: " + jPrimitive); + public static Optional getAsOptionalJsonObject(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsJsonObject(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); } - return jPrimitive.getAsString(); } - public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement); - if (!jPrimitive.isBoolean()) { - throw new OpenemsException("This is not a Boolean: " + jPrimitive); + public static Optional getAsOptionalLong(JsonElement jElement, String memberName) { + try { + return Optional.of(getAsLong(jElement, memberName)); + } catch (OpenemsException e) { + return Optional.empty(); } - return jPrimitive.getAsBoolean(); } public static Optional getAsOptionalString(JsonElement jElement, String memberName) { @@ -116,47 +200,104 @@ public static Optional getAsOptionalString(JsonElement jElement, String } } - public static String getAsString(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isString()) { - throw new OpenemsException("Element [" + memberName + "] is not a String: " + jPrimitive); + public static Object getAsType(Class type, JsonElement j) throws NotImplementedException { + try { + if (Integer.class.isAssignableFrom(type)) { + /* + * Asking for an Integer + */ + return j.getAsInt(); + + } else if (Long.class.isAssignableFrom(type)) { + /* + * Asking for an Long + */ + return j.getAsLong(); + } else if (Boolean.class.isAssignableFrom(type)) { + /* + * Asking for an Boolean + */ + return j.getAsBoolean(); + } else if (Double.class.isAssignableFrom(type)) { + /* + * Asking for an Double + */ + return j.getAsDouble(); + } else if (String.class.isAssignableFrom(type)) { + /* + * Asking for a String + */ + return j.getAsString(); + } else if (JsonObject.class.isAssignableFrom(type)) { + /* + * Asking for a JsonObject + */ + return j.getAsJsonObject(); + } else if (JsonArray.class.isAssignableFrom(type)) { + /* + * Asking for a JsonArray + */ + return j.getAsJsonArray(); + } else if (type.isArray()){ + /** + * Asking for Array + */ + if(Long.class.isAssignableFrom(type.getComponentType())){ + /** + * Asking for ArrayOfLong + */ + if(j.isJsonArray()){ + JsonArray js = j.getAsJsonArray(); + Long[] la = new Long[js.size()]; + for(int i = 0; i < js.size(); i++){ + la[i] = js.get(i).getAsLong(); + } + return la; + } + + } + } + } catch (IllegalStateException e) { + throw new IllegalStateException("Failed to parse JsonElement [" + j + "]", e); } - return jPrimitive.getAsString(); + throw new NotImplementedException( + "Converter for value [" + j + "] to class type [" + type + "] is not implemented."); } - public static Optional getAsOptionalInt(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsInt(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); + public static Object getAsType(Optional> typeOptional, JsonElement j) throws NotImplementedException { + if (!typeOptional.isPresent()) { + throw new NotImplementedException("Type of Channel was not set: " + j.getAsString()); } + Class type = typeOptional.get(); + return getAsType(type, j); } - public static Optional getAsOptionalLong(JsonElement jElement, String memberName) { - try { - return Optional.of(getAsLong(jElement, memberName)); - } catch (OpenemsException e) { - return Optional.empty(); + public static JsonPrimitive getAsPrimitive(JsonElement jElement) throws OpenemsException { + if (!jElement.isJsonPrimitive()) { + throw new OpenemsException("This is not a JsonPrimitive: " + jElement); } + return jElement.getAsJsonPrimitive(); } - public static int getAsInt(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsInt(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Integer.parseInt(string); + public static JsonPrimitive getAsPrimitive(JsonElement jElement, String memberName) throws OpenemsException { + JsonElement jSubElement = getSubElement(jElement, memberName); + return getAsPrimitive(jSubElement); + } + + public static String getAsString(JsonElement jElement) throws OpenemsException { + JsonPrimitive jPrimitive = getAsPrimitive(jElement); + if (!jPrimitive.isString()) { + throw new OpenemsException("This is not a String: " + jPrimitive); } - throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); + return jPrimitive.getAsString(); } - public static boolean getAsBoolean(JsonElement jElement, String memberName) throws OpenemsException { + public static String getAsString(JsonElement jElement, String memberName) throws OpenemsException { JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (!jPrimitive.isBoolean()) { - throw new OpenemsException("Element [" + memberName + "] is not a Boolean: " + jPrimitive); + if (!jPrimitive.isString()) { + throw new OpenemsException("Element [" + memberName + "] is not a String: " + jPrimitive); } - return jPrimitive.getAsBoolean(); + return jPrimitive.getAsString(); } /** @@ -182,15 +323,35 @@ public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memb } } - public static long getAsLong(JsonElement jElement, String memberName) throws OpenemsException { - JsonPrimitive jPrimitive = getAsPrimitive(jElement, memberName); - if (jPrimitive.isNumber()) { - return jPrimitive.getAsLong(); - } else if (jPrimitive.isString()) { - String string = jPrimitive.getAsString(); - return Long.parseLong(string); + public static Set getMatchingElements(JsonElement j, String... paths) { + Set result = new HashSet(); + if (paths.length == 0) { + // last path element + result.add(j); + return result; } - throw new OpenemsException("[" + memberName + "] is not a Number: " + jPrimitive); + String path = paths[0]; + if (j.isJsonObject()) { + JsonObject jO = j.getAsJsonObject(); + if (jO.has(path)) { + List nextPathsList = new ArrayList(Arrays.asList(paths)); + nextPathsList.remove(0); + String[] nextPaths = nextPathsList.toArray(new String[0]); + result.addAll(getMatchingElements(jO.get(path), nextPaths)); + } + } else if (j.isJsonArray()) { + for (JsonElement jE : j.getAsJsonArray()) { + result.addAll(getMatchingElements(jE, paths)); + } + } else if (j.isJsonPrimitive()) { + JsonPrimitive jP = j.getAsJsonPrimitive(); + if (jP.isString()) { + if (jP.getAsString().equals(path)) { + result.add(jP); + } + } + } + return result; } public static JsonElement getSubElement(JsonElement jElement, String memberName) throws OpenemsException { @@ -201,6 +362,10 @@ public static JsonElement getSubElement(JsonElement jElement, String memberName) return jObject.get(memberName); } + public static boolean hasElement(JsonElement j, String... paths) { + return getMatchingElements(j, paths).size() > 0; + } + /** * Merges the second Object into the first object * @@ -226,52 +391,6 @@ public static Optional merge(Optional j1Opt, Optional 0; - } - - public static Set getMatchingElements(JsonElement j, String... paths) { - Set result = new HashSet(); - if (paths.length == 0) { - // last path element - result.add(j); - return result; - } - String path = paths[0]; - if (j.isJsonObject()) { - JsonObject jO = j.getAsJsonObject(); - if (jO.has(path)) { - List nextPathsList = new ArrayList(Arrays.asList(paths)); - nextPathsList.remove(0); - String[] nextPaths = nextPathsList.toArray(new String[0]); - result.addAll(getMatchingElements(jO.get(path), nextPaths)); - } - } else if (j.isJsonArray()) { - for (JsonElement jE : j.getAsJsonArray()) { - result.addAll(getMatchingElements(jE, paths)); - } - } else if (j.isJsonPrimitive()) { - JsonPrimitive jP = j.getAsJsonPrimitive(); - if (jP.isString()) { - if (jP.getAsString().equals(path)) { - result.add(jP); - } - } - } - return result; - } - - /** - * Pretty print a JsonElement - * - * @param j - */ - public static void prettyPrint(JsonElement j) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(j); - System.out.println(json); - } - /** * Parses a string to a JsonElement * @@ -287,64 +406,14 @@ public static JsonElement parse(String string) throws OpenemsException { } } - /* - * Copied from edge TODO! + /** + * Pretty print a JsonElement + * + * @param j */ - public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { - // null - if (value == null) { - return null; - } - // optional - if (value instanceof Optional) { - if (!((Optional) value).isPresent()) { - return null; - } else { - value = ((Optional) value).get(); - } - } - if (value instanceof Number) { - /* - * Number - */ - return new JsonPrimitive((Number) value); - } else if(value instanceof ChannelEnum) { - /* - * ChannelEnum - */ - return new JsonPrimitive(((ChannelEnum)value).getValue()); - } else if (value instanceof String) { - /* - * String - */ - return new JsonPrimitive((String) value); - } else if (value instanceof Boolean) { - /* - * Boolean - */ - return new JsonPrimitive((Boolean) value); - } else if (value instanceof Inet4Address) { - /* - * Inet4Address - */ - return new JsonPrimitive(((Inet4Address) value).getHostAddress()); - } else if (value instanceof JsonElement) { - /* - * JsonElement - */ - return (JsonElement) value; - } else if (value instanceof Long[]){ - /* - * Long-Array - */ - JsonArray js = new JsonArray(); - for (Long l : (Long[]) value){ - js.add(new JsonPrimitive((Long) l)); - } - return js; - } - throw new NotImplementedException("Converter for [" + value + "]" + " of type [" // - + value.getClass().getSimpleName() + "]" // - + " to JSON is not implemented."); + public static void prettyPrint(JsonElement j) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(j); + System.out.println(json); } } From 74786aaf778c390b0fd4576d49e4389aad199dd7 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 25 Feb 2018 02:18:50 +0100 Subject: [PATCH 100/156] Implement config via one Json file --- .../BackendApp.bndrun | 2 +- .../impl/provider/EdgeWebsocket.java | 2 +- io.openems.common/bnd.bnd | 6 +- .../src/io/openems/common/config/Config.java | 29 +++++ .../common/config/ConfigEnumeration.java | 26 ++++ .../io/openems/common/config/ConfigUtils.java | 61 +++++++++ .../common/config/JsonPersistenceManager.java | 121 ++++++++++++++++++ .../openems/common/config/package-info.java | 2 + .../io/openems/common/utils/JsonUtils.java | 39 ++++-- 9 files changed, 274 insertions(+), 14 deletions(-) create mode 100644 io.openems.common/src/io/openems/common/config/Config.java create mode 100644 io.openems.common/src/io/openems/common/config/ConfigEnumeration.java create mode 100644 io.openems.common/src/io/openems/common/config/ConfigUtils.java create mode 100644 io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java create mode 100644 io.openems.common/src/io/openems/common/config/package-info.java diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index af8f1a1f49b..31016c7e446 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -12,7 +12,7 @@ JPM-Command: openems-backend osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-service)',\ osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-api)' -runproperties: \ - felix.cm.dir=C:/openems-config,\ + configFile=C:/openems-config/backend.json,\ org.ops4j.pax.logging.service.frameworkEventsLogLevel="DISABLED" #-runproperties: felix.cm.dir=/etc/openems.d -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 483e938b3de..082c9f65016 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -66,7 +66,7 @@ private synchronized void stopServer() { if (this.server != null) { try { this.server.stop(); - } catch (IOException | InterruptedException e) { + } catch (NullPointerException | IOException | InterruptedException e) { log.error("Unable to stop existing EdgeWebsocketServer: " + e.getMessage()); } } diff --git a/io.openems.common/bnd.bnd b/io.openems.common/bnd.bnd index e6ebe3d7754..910f4a389fe 100644 --- a/io.openems.common/bnd.bnd +++ b/io.openems.common/bnd.bnd @@ -10,7 +10,8 @@ Export-Package: \ io.openems.common.exceptions,\ io.openems.common.types,\ io.openems.common.api,\ - io.openems.common.websocket + io.openems.common.websocket,\ + io.openems.common.config -includeresource: {readme.md} @@ -18,7 +19,8 @@ Export-Package: \ osgi.enroute.base.api;version=2.1,\ com.google.guava,\ io.openems.wrapper.websocket;version=latest,\ - com.google.gson;version=2.8 + com.google.gson;version=2.8,\ + org.apache.felix.configadmin;version=1.8 -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.common/src/io/openems/common/config/Config.java b/io.openems.common/src/io/openems/common/config/Config.java new file mode 100644 index 00000000000..5fa17bc95b6 --- /dev/null +++ b/io.openems.common/src/io/openems/common/config/Config.java @@ -0,0 +1,29 @@ +package io.openems.common.config; + +import java.util.Hashtable; + +public class Config extends Hashtable { + + // private final Logger log = LoggerFactory.getLogger(Config.class); + + private final String pid; + + public Config(String pid) { + this.pid = pid; + this.put("service.pid", pid); + } + + public String getPid() { + return pid; + } + + private static final long serialVersionUID = 1L; + + @Override + public synchronized Object get(Object key) { + Object o = super.get(key); + // log.debug("Reading from Config PID [" + this.getPid() + "]: [" + key + "=" + + // o + "]"); + return o; + } +} diff --git a/io.openems.common/src/io/openems/common/config/ConfigEnumeration.java b/io.openems.common/src/io/openems/common/config/ConfigEnumeration.java new file mode 100644 index 00000000000..1868b33a7cc --- /dev/null +++ b/io.openems.common/src/io/openems/common/config/ConfigEnumeration.java @@ -0,0 +1,26 @@ +package io.openems.common.config; + +import java.util.Enumeration; +import java.util.Iterator; + +class ConfigEnumeration implements Enumeration { + + // private final Logger log = LoggerFactory.getLogger(ConfigEnumeration.class); + + private final Iterator iterator; + + public ConfigEnumeration(Iterator iterator) { + this.iterator = iterator; + } + + public Config nextElement() { + Config config = iterator.next(); + // log.debug("Reading Config for PID ["+config.getPid()+"]"); + return config; + } + + public boolean hasMoreElements() { + return iterator.hasNext(); + } + +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/config/ConfigUtils.java b/io.openems.common/src/io/openems/common/config/ConfigUtils.java new file mode 100644 index 00000000000..4f80ee7fa87 --- /dev/null +++ b/io.openems.common/src/io/openems/common/config/ConfigUtils.java @@ -0,0 +1,61 @@ +package io.openems.common.config; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.TreeMap;import com.google.common.eventbus.SubscriberExceptionContext; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.NotImplementedException; +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.Log; + +public class ConfigUtils { + + private final static Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + protected static synchronized JsonObject readConfigFromFile(Path path) throws Exception { + String config = new String(Files.readAllBytes(path), DEFAULT_CHARSET); + JsonElement jsonElement = JsonUtils.parse(config); + return jsonElement.getAsJsonObject(); + } + + protected static synchronized void writeConfigToFile(Path path, TreeMap configs) + throws IOException { + // create JsonObject + JsonObject j = new JsonObject(); + for (Entry entry : configs.entrySet()) { + JsonObject jSub = new JsonObject(); + // sort map by key to be able to write the json sorted + TreeMap sortedSub = new TreeMap<>(); + for (Entry subEntry : entry.getValue().entrySet()) { + sortedSub.put(subEntry.getKey(), subEntry.getValue()); + } + + for (Entry subEntry : sortedSub.entrySet()) { + if(subEntry.getKey().equals("service.pid")) { + // ignore. It's already the key of the JsonObject + continue; + } + try { + jSub.add(subEntry.getKey(), JsonUtils.getAsJsonElement(subEntry.getValue())); + } catch (NotImplementedException e) { + Log.warn("Unable to store [" + entry.getKey() + "/" + subEntry.getKey() + "] value [" + + subEntry.getValue() + "] in config: " + e.getMessage()); + } + } + j.add(entry.getKey(), jSub); + } + + // write to file + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String config = gson.toJson(j); + Files.write(path, config.getBytes(DEFAULT_CHARSET)); + } +} diff --git a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java new file mode 100644 index 00000000000..e0342779dd1 --- /dev/null +++ b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java @@ -0,0 +1,121 @@ +package io.openems.common.config; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.apache.felix.cm.PersistenceManager; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.utils.JsonUtils; + +@Component() +public class JsonPersistenceManager implements PersistenceManager { + + private final Logger log = LoggerFactory.getLogger(JsonPersistenceManager.class); + + private static final Path configFile = Paths.get(System.getProperty("configFile")); + + private final TreeMap configs = new TreeMap<>(); + + @Activate + void activate() { + // read Json from file + JsonObject jConfig; + try { + jConfig = ConfigUtils.readConfigFromFile(configFile); + } catch (Exception e) { + log.error(e.getMessage()); + return; + } + + // parse config + fill configMap + parseJsonToConfigMap(jConfig); + } + + @Deactivate + void deactivate() { + log.info("deactivate config"); + this.configs.clear(); + } + + @Override + public void delete(String arg0) throws IOException { + log.info("Delete " + arg0); + } + + @Override + public boolean exists(String arg0) { + log.info("Exists " + arg0); + return false; + } + + @Override + public Enumeration getDictionaries() throws IOException { + return new ConfigEnumeration(this.configs.values().iterator()); + } + + @SuppressWarnings("rawtypes") + @Override + public Dictionary load(String arg0) throws IOException { + log.info("load " + arg0); + return null; + } + + @Override + public void store(String pid, @SuppressWarnings("rawtypes") Dictionary values) throws IOException { + log.debug("Store to Config. PID [" + pid + "]: " + values); + + synchronized (this.configs) { + Config config = this.configs.get(pid); + if (config == null) { + config = new Config(pid); + } + Enumeration keys = values.keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + config.put(key, values.get(key)); + } + this.configs.put(pid, config); + + this.saveConfigMapToFile(); + } + } + + private void parseJsonToConfigMap(JsonObject jConfig) { + synchronized (this.configs) { + for (Entry configEntry : jConfig.entrySet()) { + Config thisConfig = new Config(configEntry.getKey()); + if (configEntry.getValue().isJsonObject()) { + JsonObject jThisConfig = configEntry.getValue().getAsJsonObject(); + for (Entry thisConfigEntry : jThisConfig.entrySet()) { + thisConfig.put(thisConfigEntry.getKey(), JsonUtils.getAsBestType(thisConfigEntry.getValue())); + } + } + this.configs.put(thisConfig.getPid(), thisConfig); + } + } + } + + private void saveConfigMapToFile() { + synchronized (this.configs) { + try { + ConfigUtils.writeConfigToFile(configFile, configs); + } catch (IOException e) { + log.error("Unable to write config to file: " + e.getMessage()); + } + } + } +} diff --git a/io.openems.common/src/io/openems/common/config/package-info.java b/io.openems.common/src/io/openems/common/config/package-info.java new file mode 100644 index 00000000000..8e1f225ce98 --- /dev/null +++ b/io.openems.common/src/io/openems/common/config/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.common.config; \ No newline at end of file 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 72329ea6afc..30877a8a652 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -52,7 +52,7 @@ public static int getAsInt(JsonElement jElement, String memberName) throws Opene } throw new OpenemsException("Element [" + memberName + "] is not an Integer: " + jPrimitive); } - + public static JsonArray getAsJsonArray(JsonElement jElement) throws OpenemsException { if (!jElement.isJsonArray()) { throw new OpenemsException("This is not a JsonArray: " + jElement); @@ -67,7 +67,7 @@ public static JsonArray getAsJsonArray(JsonElement jElement, String memberName) } return jSubElement.getAsJsonArray(); }; - + public static JsonElement getAsJsonElement(Object value) throws NotImplementedException { // null if (value == null) { @@ -86,11 +86,11 @@ public static JsonElement getAsJsonElement(Object value) throws NotImplementedEx * Number */ return new JsonPrimitive((Number) value); - } else if(value instanceof ChannelEnum) { + } else if (value instanceof ChannelEnum) { /* * ChannelEnum */ - return new JsonPrimitive(((ChannelEnum)value).getValue()); + return new JsonPrimitive(((ChannelEnum) value).getValue()); } else if (value instanceof String) { /* * String @@ -111,12 +111,12 @@ public static JsonElement getAsJsonElement(Object value) throws NotImplementedEx * JsonElement */ return (JsonElement) value; - } else if (value instanceof Long[]){ + } else if (value instanceof Long[]) { /* * Long-Array */ JsonArray js = new JsonArray(); - for (Long l : (Long[]) value){ + for (Long l : (Long[]) value) { js.add(new JsonPrimitive((Long) l)); } return js; @@ -200,6 +200,25 @@ public static Optional getAsOptionalString(JsonElement jElement, String } } + public static Object getAsBestType(JsonElement j) { + try { + if (!j.isJsonPrimitive()) { + return j.toString(); + } + JsonPrimitive jP = j.getAsJsonPrimitive(); + if (jP.isBoolean()) { + return jP.getAsBoolean(); + } + if (jP.isNumber()) { + Number n = jP.getAsNumber(); + return n.intValue(); + } + return j.getAsString(); + } catch (IllegalStateException e) { + throw new IllegalStateException("Failed to parse JsonElement [" + j + "]", e); + } + } + public static Object getAsType(Class type, JsonElement j) throws NotImplementedException { try { if (Integer.class.isAssignableFrom(type)) { @@ -238,18 +257,18 @@ public static Object getAsType(Class type, JsonElement j) throws NotImplement * Asking for a JsonArray */ return j.getAsJsonArray(); - } else if (type.isArray()){ + } else if (type.isArray()) { /** * Asking for Array */ - if(Long.class.isAssignableFrom(type.getComponentType())){ + if (Long.class.isAssignableFrom(type.getComponentType())) { /** * Asking for ArrayOfLong */ - if(j.isJsonArray()){ + if (j.isJsonArray()) { JsonArray js = j.getAsJsonArray(); Long[] la = new Long[js.size()]; - for(int i = 0; i < js.size(); i++){ + for (int i = 0; i < js.size(); i++) { la[i] = js.get(i).getAsLong(); } return la; From 9d1c1e1c8e05331c5cf05100dbfff652d6efa0fc Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 25 Feb 2018 16:09:21 +0100 Subject: [PATCH 101/156] Write config file only when values actually changed --- .../BackendApp.bndrun | 2 +- .../io/openems/common/config/ConfigUtils.java | 10 ++++---- .../common/config/JsonPersistenceManager.java | 23 ++++++++++++++----- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 31016c7e446..b260e2053fe 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -19,6 +19,7 @@ JPM-Command: openems-backend -runee: JavaSE-1.8 -runbundles: \ + io.openems.common;version=snapshot,\ io.openems.backend.application;version=snapshot,\ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ org.apache.felix.log;version='[1.0.1,1.0.2)',\ @@ -29,7 +30,6 @@ JPM-Command: openems-backend org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ io.openems.wrapper.websocket;version=snapshot,\ com.google.gson;version='[2.8.2,2.8.3)',\ - io.openems.common;version=snapshot,\ io.openems.backend.metadata.odoo.provider;version=snapshot,\ io.openems.backend.edgewebsocket.impl.provider;version=snapshot,\ org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ diff --git a/io.openems.common/src/io/openems/common/config/ConfigUtils.java b/io.openems.common/src/io/openems/common/config/ConfigUtils.java index 4f80ee7fa87..4d9a2dd8f23 100644 --- a/io.openems.common/src/io/openems/common/config/ConfigUtils.java +++ b/io.openems.common/src/io/openems/common/config/ConfigUtils.java @@ -4,9 +4,9 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; import java.util.Map.Entry; -import java.util.TreeMap;import com.google.common.eventbus.SubscriberExceptionContext; +import java.util.TreeMap; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; @@ -31,15 +31,15 @@ protected static synchronized void writeConfigToFile(Path path, TreeMap entry : configs.entrySet()) { - JsonObject jSub = new JsonObject(); + JsonObject jSub = new JsonObject(); // sort map by key to be able to write the json sorted TreeMap sortedSub = new TreeMap<>(); for (Entry subEntry : entry.getValue().entrySet()) { sortedSub.put(subEntry.getKey(), subEntry.getValue()); } - + for (Entry subEntry : sortedSub.entrySet()) { - if(subEntry.getKey().equals("service.pid")) { + if (subEntry.getKey().equals("service.pid")) { // ignore. It's already the key of the JsonObject continue; } diff --git a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java index e0342779dd1..b10671fca0f 100644 --- a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java +++ b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java @@ -1,7 +1,6 @@ package io.openems.common.config; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Dictionary; @@ -78,19 +77,31 @@ public Dictionary load(String arg0) throws IOException { public void store(String pid, @SuppressWarnings("rawtypes") Dictionary values) throws IOException { log.debug("Store to Config. PID [" + pid + "]: " + values); + boolean configNeedsToBeAdded = false; + boolean configChanged = false; synchronized (this.configs) { Config config = this.configs.get(pid); if (config == null) { config = new Config(pid); + configNeedsToBeAdded = true; + configChanged = true; } Enumeration keys = values.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); - config.put(key, values.get(key)); + Object newValue = values.get(key); + Object existingValue = config.get(key); + if (existingValue == null || (existingValue != null && !existingValue.equals(newValue))) { + config.put(key, newValue); + configChanged = true; + } + } + if (configNeedsToBeAdded) { + this.configs.put(pid, config); + } + if (configChanged) { + this.saveConfigMapToFile(); } - this.configs.put(pid, config); - - this.saveConfigMapToFile(); } } @@ -108,7 +119,7 @@ private void parseJsonToConfigMap(JsonObject jConfig) { } } } - + private void saveConfigMapToFile() { synchronized (this.configs) { try { From a80458121d1281741a602eb297a1508391ead729 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 25 Feb 2018 18:30:37 +0100 Subject: [PATCH 102/156] Change messageId to json object; improve log outputs --- .../impl/provider/EdgeWebsocket.java | 3 +- .../impl/provider/EdgeWebsocketServer.java | 44 ++++++++++++++----- .../impl/provider/UiWebsocketServer.java | 39 +++++++++------- .../common/config/JsonPersistenceManager.java | 25 +++++++---- .../common/websocket/DefaultMessages.java | 12 +++++ ui/src/app/shared/device/device.ts | 6 +-- ui/src/app/shared/service/defaultmessages.ts | 22 +++++++--- ui/src/app/shared/service/defaulttypes.ts | 7 ++- ui/src/app/shared/service/websocket.ts | 4 +- 9 files changed, 112 insertions(+), 50 deletions(-) diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 082c9f65016..597e0d78a52 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -4,6 +4,7 @@ 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; @@ -21,7 +22,7 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition; @Designate(ocd = EdgeWebsocket.Config.class, factory = false) -@Component(name = "EdgeWebsocket", immediate = true) +@Component(name = "EdgeWebsocket", immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE) public class EdgeWebsocket implements EdgeWebsocketService { private final Logger log = LoggerFactory.getLogger(EdgeWebsocket.class); diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 596ab1e77b3..6756d713c91 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -121,7 +121,6 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { */ @Override protected void _onMessage(WebSocket websocket, JsonObject jMessage) { - log.info("message: " + StringUtils.toShortString(jMessage, 100)); // get edgeIds from websocket int[] edgeIds = websocket.getAttachment(); @@ -140,15 +139,17 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { log.warn(e.getMessage()); } } + return; } /* * Is this a reply? -> forward to Browser */ - if (jMessage.has("id")) { + if (jMessage.has("messageId")) { for (int edgeId : edgeIds) { this.parent.uiWebsocketService.handleEdgeReply(edgeId, jMessage); } + return; } /* @@ -157,12 +158,27 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { Optional jTimedataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "timedata"); if (jTimedataOpt.isPresent()) { timedata(edgeIds, jTimedataOpt.get()); + return; + } + + /* + * Unknown message + */ + for (String edgeName : getEdgeNames(edgeIds)) { + log.warn("Edge [" + edgeName + "] unknown message: " + StringUtils.toShortString(jMessage, 100)); } } @Override protected void _onError(WebSocket websocket, Exception ex) { - System.out.println("_onError"); + if (websocket == null) { + log.warn("Edge [UNKNOWN] websocket error: " + ex.getMessage()); + } else { + int[] edgeIds = websocket.getAttachment(); + for (String edgeName : getEdgeNames(edgeIds)) { + log.warn("Edge [" + edgeName + "] websocket error: " + ex.getMessage()); + } + } } @Override @@ -186,13 +202,8 @@ protected void _onClose(WebSocket websocket) { } // log - for (int edgeId : edgeIds) { - Optional edgeOpt = this.parent.metadataService.getEdgeOpt(edgeId); - if (edgeOpt.isPresent()) { - log.info("Device [" + edgeOpt.get().getName() + "] disconnected."); - } else { - log.info("Device [ID:" + edgeId + "] disconnected."); - } + for (String edgeName : getEdgeNames(edgeIds)) { + log.info("Edge [" + edgeName + "] disconnected."); } } @@ -251,4 +262,17 @@ private void timedata(int[] edgeIds, JsonObject jTimedata) { } } } + + private String[] getEdgeNames(int[] edgeIds) { + String[] edgeNames = new String[edgeIds.length]; + for (int i = 0; i < edgeIds.length; i++) { + Optional edgeOpt = this.parent.metadataService.getEdgeOpt(edgeIds[i]); + if (edgeOpt.isPresent()) { + edgeNames[i] = edgeOpt.get().getName(); + } else { + edgeNames[i] = "ID:" + edgeIds[i]; + } + } + return edgeNames; + } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index d0e51ce866c..c4e0ea828b3 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -95,19 +95,16 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { @Override protected void _onError(WebSocket websocket, Exception ex) { - log.info("UiWebsocketServer: On Error"); + WebsocketData data = websocket.getAttachment(); + log.info("User [" + getUserName(data) + "] websocket error: " + ex.getMessage()); } @Override protected void _onClose(WebSocket websocket) { // get current User WebsocketData data = websocket.getAttachment(); - Optional userOpt = this.parent.metadataService.getUser(data.getUserId()); - if (userOpt.isPresent()) { - log.info("User [" + userOpt.get().getName() + "] disconnected."); - } else { - log.info("User [ID:" + data.getUserId() + "] disconnected."); - } + log.info("User [" + getUserName(data) + "] disconnected."); + // stop CurrentDataWorker Optional currentDataWorkerOpt = data.getCurrentDataWorker(); if (currentDataWorkerOpt.isPresent()) { @@ -132,13 +129,13 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { User user = userOpt.get(); // get MessageId from message - Optional messageIdOpt = JsonUtils.getAsOptionalString(jMessage, "messageId"); + Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "messageId"); // get EdgeId from message Optional edgeIdOpt = JsonUtils.getAsOptionalInt(jMessage, "edgeId"); - if (messageIdOpt.isPresent() && edgeIdOpt.isPresent()) { - String messageId = messageIdOpt.get(); + if (jMessageIdOpt.isPresent() && edgeIdOpt.isPresent()) { + JsonObject jMessageId = jMessageIdOpt.get(); int edgeId = edgeIdOpt.get(); /* @@ -165,7 +162,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { Optional jHistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); if (jHistoricDataOpt.isPresent()) { JsonObject jHistoricData = jHistoricDataOpt.get(); - JsonObject jReply = this.historicData(messageId, edgeId, jHistoricData); + JsonObject jReply = this.historicData(jMessageId, edgeId, jHistoricData); WebSocketUtils.send(websocket, jReply); } @@ -177,7 +174,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { JsonObject jCurrentData = jCurrentDataOpt.get(); log.info("User [" + user.getName() + "] subscribed to current data for device [" + edge.getName() + "]: " + StringUtils.toShortString(jCurrentData, 50)); - this.currentData(websocket, data, messageId, edgeId, jCurrentData); + this.currentData(websocket, data, jMessageId, edgeId, jCurrentData); } /* @@ -191,7 +188,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * Query current config */ - JsonObject jReply = DefaultMessages.configQueryReply(new JsonObject() /* TODO */, edge.getConfig()); + JsonObject jReply = DefaultMessages.configQueryReply(jMessageId, edge.getConfig()); WebSocketUtils.send(websocket, jReply); break; } @@ -218,7 +215,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { * * @param j */ - private synchronized void currentData(WebSocket websocket, WebsocketData data, String messageId, int edgeId, + private synchronized void currentData(WebSocket websocket, WebsocketData data, JsonObject jMessageId, int edgeId, JsonObject jCurrentData) { try { String mode = JsonUtils.getAsString(jCurrentData, "mode"); @@ -248,7 +245,7 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, S } if (!channels.isEmpty()) { // create new worker - BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, new JsonObject() /* TODO */, edgeId, + BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, jMessageId, edgeId, channels); data.setCurrentDataWorker(worker); } @@ -264,7 +261,7 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, S * * @param j */ - private JsonObject historicData(String jMessageId, int edgeId, JsonObject jHistoricData) { + private JsonObject historicData(JsonObject jMessageId, int edgeId, JsonObject jHistoricData) { try { String mode = JsonUtils.getAsString(jHistoricData, "mode"); @@ -292,7 +289,7 @@ private JsonObject historicData(String jMessageId, int edgeId, JsonObject jHisto JsonArray jData = this.parent.timeDataService.queryHistoricData(edgeId, fromDate, toDate, channels, resolution); // send reply - return DefaultMessages.historicDataQueryReply(new JsonObject() /* TODO */, jData); + return DefaultMessages.historicDataQueryReply(jMessageId, jData); } } catch (Exception e) { // TODO handle exception @@ -301,4 +298,12 @@ private JsonObject historicData(String jMessageId, int edgeId, JsonObject jHisto return new JsonObject(); } + private String getUserName(WebsocketData data) { + Optional userOpt = this.parent.metadataService.getUser(data.getUserId()); + if (userOpt.isPresent()) { + return userOpt.get().getName(); + } else { + return "ID:" + data.getUserId(); + } + } } diff --git a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java index b10671fca0f..99430e80bec 100644 --- a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java +++ b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java @@ -20,7 +20,7 @@ import io.openems.common.utils.JsonUtils; -@Component() +@Component(property = "ranking=100") public class JsonPersistenceManager implements PersistenceManager { private final Logger log = LoggerFactory.getLogger(JsonPersistenceManager.class); @@ -51,14 +51,20 @@ void deactivate() { } @Override - public void delete(String arg0) throws IOException { - log.info("Delete " + arg0); + public void delete(String pid) throws IOException { + log.debug("Delete configuration for PID [" + pid + "]"); + synchronized (this.configs) { + if (this.configs.remove(pid) != null) { + this.saveConfigMapToFile(); + } + } } @Override - public boolean exists(String arg0) { - log.info("Exists " + arg0); - return false; + public boolean exists(String pid) { + synchronized (this.configs) { + return this.configs.containsKey(pid); + } } @Override @@ -68,9 +74,10 @@ public Enumeration getDictionaries() throws IOException { @SuppressWarnings("rawtypes") @Override - public Dictionary load(String arg0) throws IOException { - log.info("load " + arg0); - return null; + public Dictionary load(String pid) throws IOException { + synchronized (this.configs) { + return this.configs.get(pid); + } } @Override diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 3f9bf056c71..3db36d2a042 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -14,6 +14,18 @@ public class DefaultMessages { + /** + *
        +	 * 	{
        +	 * 		messageId: {
        +	 * 			ui: UUID,
        +	 * 			backend?: UUID
        +	 *		}
        +	 * 	}
        +	 * 
        + * @param jMessageId + * @return + */ private static JsonObject newMessage(JsonObject jMessageId) { JsonObject j = new JsonObject(); j.add("messageId", jMessageId); diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts index 4c5377ea08e..f97249bfe38 100644 --- a/ui/src/app/shared/device/device.ts +++ b/ui/src/app/shared/device/device.ts @@ -71,7 +71,7 @@ export class Device { */ public refreshConfig(): BehaviorSubject { let message = DefaultMessages.configQuery(this.edgeId); - let messageId = message.messageId; + let messageId = message.messageId.ui; this.replyStreams[messageId] = new Subject(); this.send(message); // wait for reply @@ -94,14 +94,14 @@ export class Device { } private sendMessageWithReply(message: DefaultTypes.IdentifiedMessage): Subject { - let messageId: string = message.messageId; + let messageId: string = message.messageId.ui; this.replyStreams[messageId] = new Subject(); this.send(message); return this.replyStreams[messageId]; } private removeReplyStream(reply: DefaultMessages.Reply) { - let messageId: string = reply.messageId; + let messageId: string = reply.messageId.ui; this.replyStreams[messageId].unsubscribe(); delete this.replyStreams[messageId]; } diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts index e20333164b4..1725c5b7687 100644 --- a/ui/src/app/shared/service/defaultmessages.ts +++ b/ui/src/app/shared/service/defaultmessages.ts @@ -24,9 +24,11 @@ export class DefaultMessages { }; }; - public static configQuery(edgeId: number) { + public static configQuery(edgeId: number): DefaultTypes.IdentifiedMessage { return { - messageId: UUID.UUID(), + messageId: { + ui: UUID.UUID() + }, edgeId: edgeId, config: { mode: "query", @@ -37,7 +39,9 @@ export class DefaultMessages { public static configUpdate(thingId: string, channelId: string, value: any): DefaultTypes.ConfigUpdate { return { - messageId: UUID.UUID(), + messageId: { + ui: UUID.UUID() + }, config: { mode: "update", thing: thingId, @@ -47,9 +51,11 @@ export class DefaultMessages { } } - public static currentDataSubscribe(edgeId: number, channels: DefaultTypes.ChannelAddresses) { + public static currentDataSubscribe(edgeId: number, channels: DefaultTypes.ChannelAddresses): DefaultTypes.IdentifiedMessage { return { - messageId: UUID.UUID(), + messageId: { + ui: UUID.UUID() + }, edgeId: edgeId, currentData: { mode: "subscribe", @@ -58,9 +64,11 @@ export class DefaultMessages { } }; - public static historicDataQuery(edgeId: number, fromDate: Date, toDate: Date, timezone: number /*offset in seconds*/, channels: DefaultTypes.ChannelAddresses) { + public static historicDataQuery(edgeId: number, fromDate: Date, toDate: Date, timezone: number /*offset in seconds*/, channels: DefaultTypes.ChannelAddresses): DefaultTypes.IdentifiedMessage { return { - messageId: UUID.UUID(), + messageId: { + ui: UUID.UUID() + }, edgeId: edgeId, historicData: { mode: "query", diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index 2c307f6f851..9243928ec15 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -110,7 +110,12 @@ export module DefaultTypes { export type LanguageTag = "de" | "en" | "cz" | "nl"; export interface IdentifiedMessage { - messageId: string + messageId: { + ui: string, + backend?: string + }, + edgeId?: number, + [thing: string]: {} } export interface ConfigUpdate extends IdentifiedMessage { diff --git a/ui/src/app/shared/service/websocket.ts b/ui/src/app/shared/service/websocket.ts index e61db474f76..75b464afda7 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -205,9 +205,9 @@ export class Websocket { /* * Query reply */ - if ("messageId" in message) { + if ("messageId" in message && "ui" in message.messageId) { // Receive a reply with a message id -> find device and forward to devices' replyStream - let messageId = message.messageId; + let messageId = message.messageId.ui; for (let deviceName in this.replyStreams) { if (messageId in this.replyStreams[deviceName]) { this.replyStreams[deviceName][messageId].next(message); From 0649ffe44343bc7e704f50cb4a216d1d1c926650 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 25 Feb 2018 22:38:45 +0100 Subject: [PATCH 103/156] Reimplement communication UI -> Backend -> Edge -> Backend -> UI --- .../websocket/EdgeCurrentDataWorker.java | 5 +- .../websocket/EdgeWebsocketHandler.java | 322 +++++++++--------- .../fenecon/FeneconPersistence.java | 19 +- .../backend/application/BackendApp.java | 2 +- io.openems.backend.edgewebsocket.api/bnd.bnd | 6 +- .../api/EdgeWebsocketService.java | 6 + .../impl/provider/EdgeWebsocket.java | 8 + .../impl/provider/EdgeWebsocketServer.java | 9 +- .../impl/provider/UiWebsocket.java | 5 +- .../impl/provider/UiWebsocketServer.java | 56 ++- .../common/websocket/CurrentDataWorker.java | 4 +- .../common/websocket/DefaultMessages.java | 37 +- .../common/websocket/WebSocketUtils.java | 50 ++- .../directcontrol/directcontrol.component.ts | 2 +- ui/src/app/shared/config/channel.component.ts | 4 +- ui/src/app/shared/service/defaultmessages.ts | 3 +- 16 files changed, 323 insertions(+), 215 deletions(-) diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java index 8560c523e5b..b226f217aec 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java @@ -18,7 +18,6 @@ public class EdgeCurrentDataWorker extends CurrentDataWorker { - private final EdgeWebsocketHandler edgeWebsocketHandler; private final ThingRepository thingRepository; private final Databus databus; @@ -27,12 +26,10 @@ public class EdgeCurrentDataWorker extends CurrentDataWorker { */ private final Role role; - public EdgeCurrentDataWorker(JsonObject jMessageId, HashMultimap channels, Role role, - EdgeWebsocketHandler edgeWebsocketHandler) { + public EdgeCurrentDataWorker(EdgeWebsocketHandler edgeWebsocketHandler, JsonObject jMessageId, HashMultimap channels, Role role) { // TODO make sure websocket is present super(edgeWebsocketHandler.getWebsocket().get(), jMessageId, channels); this.role = role; - this.edgeWebsocketHandler = edgeWebsocketHandler; this.thingRepository = ThingRepository.getInstance(); this.databus = Databus.getInstance(); } diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java index ea045e53ab9..b3383b136f1 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -124,10 +125,11 @@ public Optional getWebsocket() { * @param jMessage */ public final void onMessage(JsonObject jMessage) { - // get message id -> used for reply - Optional jIdOpt = JsonUtils.getAsOptionalJsonArray(jMessage, "id"); + // get MessageId from message -> used for reply + Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "messageId"); // get role + // TODO Role role; if (this.roleOpt.isPresent()) { role = this.roleOpt.get(); @@ -140,90 +142,85 @@ public final void onMessage(JsonObject jMessage) { } } - // prepare reply (every reply is going to be merged into this object with this unique message id) - JsonObject jReply = new JsonObject(); - - // init deviceId as empty. It's only needed in backend - Optional deviceIdOpt = Optional.empty(); - - /* - * Config - */ - Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); - if (jConfigOpt.isPresent()) { - jReply = JsonUtils.merge(jReply, // - config(jConfigOpt.get(), role, jIdOpt.orElse(new JsonArray()), apiWorkerOpt) // - ); - } - - /* - * Subscribe to currentData - */ - Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); - if (jCurrentDataOpt.isPresent() && jIdOpt.isPresent()) { - jReply = JsonUtils.merge(jReply, // - currentData(jIdOpt.get(), jCurrentDataOpt.get(), role) // - ); - } - - /* - * Query historic data - */ - // Optional jMessageIdOpt, String deviceName, WebSocket websocket, JsonElement jHistoricDataElement - Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); - if (jhistoricDataOpt.isPresent() && jIdOpt.isPresent()) { - // select first QueryablePersistence (by default the running InfluxdbPersistence) - TimedataService timedataSource = null; - for (QueryablePersistence queryablePersistence : ThingRepository.getInstance().getQueryablePersistences()) { - timedataSource = queryablePersistence; - break; - } - if (timedataSource == null) { - // TODO create notification that there is no datasource available - } else { - // TODO - // jReply = JsonUtils.merge(jReply, // - // WebSocketUtils.historicData(jIdOpt.get(), jhistoricDataOpt.get(), deviceIdOpt, timedataSource, - // role) // - // ); + // init edgeId as empty. It's only needed in backend + Optional edgeIdOpt = Optional.empty(); + + if (jMessageIdOpt.isPresent()) { + JsonObject jMessageId = jMessageIdOpt.get(); + /* + * Config + */ + Optional jConfigOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "config"); + if (jConfigOpt.isPresent()) { + this.config(role, jMessageId, apiWorkerOpt, jConfigOpt.get()); + return; } - } - /* - * Subscribe to log - */ - Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log"); - if (jLogOpt.isPresent() && jIdOpt.isPresent()) { - try { - jReply = JsonUtils.merge(jReply, // - log(jIdOpt.get(), jLogOpt.get(), role) // - ); - } catch (AccessDeniedException e) { /* ignore */ } - } + /* + * Subscribe to currentData + */ + Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); + if (jCurrentDataOpt.isPresent()) { + currentData(role, jMessageId, jCurrentDataOpt.get()); + return; + } - /* - * Remote system control - */ - { - Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system"); - if (jSystemOpt.isPresent() && jSystemOpt.isPresent()) { - try { - jReply = JsonUtils.merge(jReply, // - system(jIdOpt.get(), jSystemOpt.get(), role) // - ); - } catch (AccessDeniedException e) { - // TODO create notification - log.error(e.getMessage()); + /* + * Query historic data + */ + // Optional jMessageIdOpt, String deviceName, WebSocket websocket, JsonElement + // jHistoricDataElement + Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); + if (jhistoricDataOpt.isPresent()) { + // select first QueryablePersistence (by default the running InfluxdbPersistence) + TimedataService timedataSource = null; + for (QueryablePersistence queryablePersistence : ThingRepository.getInstance() + .getQueryablePersistences()) { + timedataSource = queryablePersistence; + break; + } + if (timedataSource == null) { + // TODO create notification that there is no datasource available + } else { + // TODO + // jReply = JsonUtils.merge(jReply, // + // WebSocketUtils.historicData(jIdOpt.get(), jhistoricDataOpt.get(), deviceIdOpt, timedataSource, + // role) // + // ); } + return; } - } - // send reply - if (jReply.entrySet().size() > 0) { - if (jIdOpt.isPresent()) { - jReply.add("id", jIdOpt.get()); - } - WebSocketUtils.send(this.websocketOpt, jReply); + /* + * Subscribe to log + */ + // TODO + // Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log"); + // if (jLogOpt.isPresent() && jIdOpt.isPresent()) { + // try { + // jReply = JsonUtils.merge(jReply, // + // log(jIdOpt.get(), jLogOpt.get(), role) // + // ); + // } catch (AccessDeniedException e) { /* ignore */ } + // } + + /* + * Remote system control + */ + // TODO + // { + // Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system"); + // if (jSystemOpt.isPresent() && jSystemOpt.isPresent()) { + // try { + // jReply = JsonUtils.merge(jReply, // + // system(jIdOpt.get(), jSystemOpt.get(), role) // + // ); + // } catch (AccessDeniedException e) { + // // TODO create notification + // log.error(e.getMessage()); + // } + // } + // } } } @@ -233,80 +230,85 @@ public final void onMessage(JsonObject jMessage) { * @param jConfig * @return */ - private synchronized JsonObject config(JsonObject jConfig, Role role, JsonArray jId, - Optional apiWorkerOpt) { - try { - String mode = JsonUtils.getAsString(jConfig, "mode"); - - if (mode.equals("query")) { - /* - * Query current config - */ + private synchronized void config(Role role, JsonObject jMessageId, Optional apiWorkerOpt, + JsonObject jConfig) { + Optional modeOpt = JsonUtils.getAsOptionalString(jConfig, "mode"); + switch (modeOpt.orElse("")) { + case "query": + /* + * Query current config + */ + try { String language = JsonUtils.getAsString(jConfig, "language"); JsonObject jReplyConfig = Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, role, language); - return DefaultMessages.configQueryReply(new JsonObject() /* TODO */, jReplyConfig); - - } else if (mode.equals("update")) { - try { - /* - * Update thing/channel config - */ - String thingId = JsonUtils.getAsString(jConfig, "thing"); - String channelId = JsonUtils.getAsString(jConfig, "channel"); - JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); - Optional channelOpt = ThingRepository.getInstance().getChannel(thingId, channelId); - if (channelOpt.isPresent()) { - Channel channel = channelOpt.get(); - // check write permissions - channel.assertWriteAllowed(role); - if (channel instanceof ConfigChannel) { - /* - * ConfigChannel - */ - ConfigChannel configChannel = (ConfigChannel) channel; - Object value = ConfigUtils.getConfigObject(configChannel, jValue); - configChannel.updateValue(value, true); - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue); - - } else if (channel instanceof WriteChannel) { - /* - * WriteChannel - */ - WriteChannel writeChannel = (WriteChannel) channel; - if (!apiWorkerOpt.isPresent()) { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.BACKEND_NOT_ALLOWED, "set " + channel.address() + " => " + jValue); - } else { - ApiWorker apiWorker = apiWorkerOpt.get(); - WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_SUCCESS, - "set " + channel.address() + " => " + jValue); - }).onFirstError((e) -> { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_FAILED, - "set " + channel.address() + " => " + jValue, e.getMessage()); - }).onTimeout(() -> { - WebSocketUtils.sendNotification(websocketOpt, jId, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, - "set " + channel.address() + " => " + jValue); - }); - apiWorker.addValue(writeChannel, writeObject); - } + WebSocketUtils.send(websocketOpt, DefaultMessages.configQueryReply(jMessageId, jReplyConfig)); + return; + } catch (OpenemsException e) { + // TODO notification + log.error(e.getMessage()); + } + + case "update": + /* + * Update thing/channel config + */ + Optional thingIdOpt = JsonUtils.getAsOptionalString(jConfig, "thing"); + Optional channelIdOpt = JsonUtils.getAsOptionalString(jConfig, "channel"); + try { + String thingId = thingIdOpt.get(); + String channelId = channelIdOpt.get(); + JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); + Optional channelOpt = ThingRepository.getInstance().getChannel(thingId, channelId); + if (channelOpt.isPresent()) { + Channel channel = channelOpt.get(); + // check write permissions + channel.assertWriteAllowed(role); + if (channel instanceof ConfigChannel) { + /* + * ConfigChannel + */ + ConfigChannel configChannel = (ConfigChannel) channel; + Object value = ConfigUtils.getConfigObject(configChannel, jValue); + configChannel.updateValue(value, true); + WebSocketUtils.sendNotificationOrLogError(websocketOpt, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue); + + } else if (channel instanceof WriteChannel) { + /* + * WriteChannel + */ + WriteChannel writeChannel = (WriteChannel) channel; + if (!apiWorkerOpt.isPresent()) { + WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */, + LogBehaviour.WRITE_TO_LOG, Notification.BACKEND_NOT_ALLOWED, + "set " + channel.address() + " => " + jValue); + } else { + ApiWorker apiWorker = apiWorkerOpt.get(); + WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> { + WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */, + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS, + "set " + channel.address() + " => " + jValue); + }).onFirstError((e) -> { + WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */, + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_FAILED, + "set " + channel.address() + " => " + jValue, e.getMessage()); + }).onTimeout(() -> { + WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */, + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, + "set " + channel.address() + " => " + jValue); + }); + apiWorker.addValue(writeChannel, writeObject); } - } else { - throw new OpenemsException("Unable to find Channel [" + thingId + "/" + channelId + "]"); } - } catch (OpenemsException e) { - WebSocketUtils.send(websocketOpt, - DefaultMessages.notification(new JsonObject() /* TODO */, Notification.EDGE_CHANNEL_UPDATE_FAILED, e.getMessage())); + } else { + throw new OpenemsException("Unable to find Channel [" + thingId + "/" + channelId + "]"); } + } catch (NoSuchElementException | OpenemsException e) { + WebSocketUtils.sendNotificationOrLogError(websocketOpt, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_FAILED, + thingIdOpt.orElse("UNDEFINED") + "/" + channelIdOpt.orElse("UNDEFINED"), e.getMessage()); } - } catch (OpenemsException e) { - log.warn(e.getMessage()); } - return new JsonObject(); } /** @@ -315,18 +317,18 @@ private synchronized JsonObject config(JsonObject jConfig, Role role, JsonArray * * @param j */ - private synchronized JsonObject currentData(JsonArray jId, JsonObject jCurrentData, Role role) { + private synchronized JsonObject currentData(Role role, JsonObject jMessageId, JsonObject jCurrentData) { try { String mode = JsonUtils.getAsString(jCurrentData, "mode"); + String messageIdUi = JsonUtils.getAsString(jMessageId, "ui"); if (mode.equals("subscribe")) { /* * Subscribe to channels */ - String messageId = jId.get(jId.size() - 1).getAsString(); // remove old worker if existed - CurrentDataWorker worker = this.currentDataSubscribers.remove(messageId); + CurrentDataWorker worker = this.currentDataSubscribers.remove(messageIdUi); if (worker != null) { worker.dispose(); } @@ -343,8 +345,8 @@ private synchronized JsonObject currentData(JsonArray jId, JsonObject jCurrentDa } if (!channels.isEmpty()) { // create new worker - worker = new EdgeCurrentDataWorker(new JsonObject() /* TODO */, channels, role, this); - this.currentDataSubscribers.put(messageId, worker); + worker = new EdgeCurrentDataWorker(this, jMessageId, channels, role); + this.currentDataSubscribers.put(messageIdUi, worker); } } } catch (OpenemsException e) { @@ -642,13 +644,23 @@ private synchronized JsonObject system(JsonArray jId, JsonObject jSystem, Role r // }).start(); // } + /** + * Send a message to the websocket. + * + * @param message + * @throws OpenemsException + */ + public void send(JsonObject j) throws OpenemsException { + WebSocketUtils.send(this.websocketOpt, j); + } + /** * Send a message to the websocket. * * @param message */ - public boolean send(JsonObject j) { - return WebSocketUtils.send(this.websocketOpt, j); + public void sendOrLogError(JsonObject j) { + WebSocketUtils.sendOrLogError(this.websocketOpt, j); } /** @@ -669,7 +681,7 @@ public void sendLog(long timestamp, String level, String source, String message) JsonObject j = DefaultMessages.log(new JsonObject() /* TODO */, timestamp, level, source, message); // TODO reevaluate if it is necessary to do this async; ie if websocket.send returns directly or not logExecutor.execute(() -> { - this.send(j); + this.sendOrLogError(j); }); } } diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index 5a78f43cf4f..db5c5eec949 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -45,10 +45,9 @@ import io.openems.api.device.nature.DeviceNature; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; -import io.openems.api.exception.ConfigException; -import io.openems.common.exceptions.NotImplementedException; import io.openems.api.persistence.Persistence; import io.openems.api.thing.Thing; +import io.openems.common.exceptions.OpenemsException; import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; import io.openems.common.types.ChannelEnum; @@ -114,10 +113,10 @@ public FeneconPersistence() { try { WebSocketUtils.send( // websocket, // - DefaultMessages.configQueryReply(new JsonObject() /* TODO */, Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, - Role.ADMIN, DEFAULT_CONFIG_LANGUAGE))); + DefaultMessages.configQueryReply(new JsonObject() /* TODO */, Config.getInstance() + .getJson(ConfigFormat.OPENEMS_UI, Role.ADMIN, DEFAULT_CONFIG_LANGUAGE))); log.info("Sent config to FENECON persistence."); - } catch (NotImplementedException | ConfigException e) { + } catch (OpenemsException e) { log.error("Unable to send config: " + e.getMessage()); } }, () -> { @@ -252,7 +251,13 @@ protected void dispose() { * @return */ private boolean send(JsonObject j) { - return this.websocketHandler.send(j); + try { + this.websocketHandler.send(j); + return true; + } catch (OpenemsException e) { + log.error(e.getMessage()); + return false; + } } /** @@ -331,7 +336,7 @@ private void addChannelValueToQueue(Channel channel, Optional valueOpt) { } else if (value instanceof Boolean) { fieldValue = new NumberFieldValue(((Boolean) value) ? 1 : 0); } else if (value instanceof ChannelEnum) { - fieldValue = new NumberFieldValue(((ChannelEnum)value).getValue()); + fieldValue = new NumberFieldValue(((ChannelEnum) value).getValue()); } else if (value instanceof DeviceNature || value instanceof JsonElement || value instanceof Map || value instanceof Set || value instanceof List || value instanceof ThingMap) { // ignore diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index c4abe1d04a7..fdadb0e3170 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -32,7 +32,7 @@ public class BackendApp { // @Reference // TimedataService timedataService; - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + @Reference private volatile EdgeWebsocketService edgeWebsocketService; @Reference diff --git a/io.openems.backend.edgewebsocket.api/bnd.bnd b/io.openems.backend.edgewebsocket.api/bnd.bnd index b74585e15b8..3429bbd683b 100644 --- a/io.openems.backend.edgewebsocket.api/bnd.bnd +++ b/io.openems.backend.edgewebsocket.api/bnd.bnd @@ -11,8 +11,10 @@ Require-Capability: \ -includeresource: {readme.md} --buildpath: \ - osgi.enroute.base.api;version=2.1 +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + com.google.gson;version=2.8,\ + io.openems.common;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java index c001d5ec8fe..a9730776ba8 100644 --- a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java +++ b/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocketService.java @@ -2,8 +2,14 @@ import org.osgi.annotation.versioning.ProviderType; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsException; + @ProviderType public interface EdgeWebsocketService { boolean isOnline(int edgeId); + + void forwardMessageFromUi(int edgeId, JsonObject jMessage) throws OpenemsException; } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 597e0d78a52..9416c46a1ab 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -13,10 +13,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonObject; + import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.timedata.api.TimedataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; +import io.openems.common.exceptions.OpenemsException; import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @@ -88,4 +91,9 @@ public boolean isOnline(int edgeId) { return this.server.isOnline(edgeId); } + @Override + public void forwardMessageFromUi(int edgeId, JsonObject jMessage) throws OpenemsException { + this.server.forwardMessageFromUi(edgeId, jMessage); + } + } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 6756d713c91..6fa5718ffe1 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -109,7 +109,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { } catch (OpenemsException e) { // send connection failed to OpenEMS JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); - WebSocketUtils.send(websocket, jReply); + WebSocketUtils.sendOrLogError(websocket, jReply); // close websocket websocket.closeConnection(CloseFrame.REFUSE, "OpenEMS connection failed. Apikey [" + apikey + "]"); } @@ -275,4 +275,11 @@ private String[] getEdgeNames(int[] edgeIds) { } return edgeNames; } + + public void forwardMessageFromUi(int edgeId, JsonObject jMessage) throws OpenemsException { + WebSocket websocket = this.websocketsMap.get(edgeId); + if (websocket != null) { + WebSocketUtils.send(websocket, jMessage); + } + } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 27a20af3cf7..8c4ef9598e8 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -33,7 +33,7 @@ public class UiWebsocket implements UiWebsocketService { protected volatile MetadataService metadataService; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) - private volatile EdgeWebsocketService edgeWebsocketService; + protected volatile EdgeWebsocketService edgeWebsocketService; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) protected volatile TimedataService timeDataService; @@ -82,7 +82,6 @@ private void startServer(int port) { @Override public void handleEdgeReply(int edgeId, JsonObject jMessage) { - // TODO Auto-generated method stub - log.info("TODO handleEdgeReply"); + this.server.handleEdgeReply(edgeId, jMessage); } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index c4e0ea828b3..7b5a9944c78 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -29,6 +29,8 @@ import io.openems.common.utils.StringUtils; import io.openems.common.websocket.AbstractWebsocketServer; import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.LogBehaviour; +import io.openems.common.websocket.Notification; import io.openems.common.websocket.WebSocketUtils; public class UiWebsocketServer extends AbstractWebsocketServer { @@ -87,10 +89,10 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { log.warn(e.getMessage()); } } + log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply("" /* TODO empty token? */, Optional.empty(), jEdges); - WebSocketUtils.send(websocket, jReply); - log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); + WebSocketUtils.sendOrLogError(websocket, jReply); } @Override @@ -163,7 +165,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { if (jHistoricDataOpt.isPresent()) { JsonObject jHistoricData = jHistoricDataOpt.get(); JsonObject jReply = this.historicData(jMessageId, edgeId, jHistoricData); - WebSocketUtils.send(websocket, jReply); + WebSocketUtils.sendOrLogError(websocket, jReply); + return; } /* @@ -175,6 +178,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { log.info("User [" + user.getName() + "] subscribed to current data for device [" + edge.getName() + "]: " + StringUtils.toShortString(jCurrentData, 50)); this.currentData(websocket, data, jMessageId, edgeId, jCurrentData); + return; } /* @@ -189,23 +193,23 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { * Query current config */ JsonObject jReply = DefaultMessages.configQueryReply(jMessageId, edge.getConfig()); - WebSocketUtils.send(websocket, jReply); - break; + WebSocketUtils.sendOrLogError(websocket, jReply); + return; } + } - /* - * TODO Forward to OpenEMS Edge - */ - // if ((jMessage.has("config") && !configModeOpt.orElse("").equals("query")) || - // jMessage.has("log") - // || jMessage.has("system")) { - // try { - // forwardMessageToOpenems(session, websocket, jMessage, deviceName); - // } catch (OpenemsException e) { - // WebSocketUtils.sendNotification(websocket, new JsonArray(), - // LogBehaviour.WRITE_TO_LOG, - // Notification.EDGE_UNABLE_TO_FORWARD, deviceName, e.getMessage()); - // } + /* + * TODO Forward to OpenEMS Edge + */ + if (jMessage.has("config") || jMessage.has("log") || jMessage.has("system")) { + try { + Optional roleOpt = user.getEdgeRole(edgeId); + JsonObject j = DefaultMessages.prepareMessageForForwardToEdge(jMessage, data.getUuid(), roleOpt); + this.parent.edgeWebsocketService.forwardMessageFromUi(edgeId, j); + } catch (OpenemsException e) { + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_UNABLE_TO_FORWARD, edge.getName(), e.getMessage()); + } } } } @@ -306,4 +310,20 @@ private String getUserName(WebsocketData data) { return "ID:" + data.getUserId(); } } + + public void handleEdgeReply(int edgeId, JsonObject jMessage) { + try { + JsonObject jMessageId = JsonUtils.getAsJsonObject(jMessage, "messageId"); + String backendId = JsonUtils.getAsString(jMessageId, "backend"); + WebSocket websocket = this.websocketsMap.get(UUID.fromString(backendId)); + if (websocket != null) { + JsonObject j = DefaultMessages.prepareMessageForForwardToUi(jMessage); + WebSocketUtils.send(websocket, j); + return; + } + throw new OpenemsException("No websocket found for UUID [" + backendId + "]"); + } catch (OpenemsException e) { + log.error("Unable to handle reply from Edge [ID:" + edgeId + "]: " + e.getMessage()); + } + } } diff --git a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java index fba72c67eca..f2f32fb48df 100644 --- a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -33,7 +33,7 @@ public abstract class CurrentDataWorker { private final ScheduledFuture future; private final WebSocket websocket; - + public CurrentDataWorker(WebSocket websocket, JsonObject jMessageId, HashMultimap channels) { this.websocket = websocket; this.channels = channels; @@ -46,7 +46,7 @@ public CurrentDataWorker(WebSocket websocket, JsonObject jMessageId, HashMultima this.dispose(); return; } - WebSocketUtils.send(this.websocket, DefaultMessages.currentData(jMessageId, getSubscribedData())); + WebSocketUtils.sendOrLogError(this.websocket, DefaultMessages.currentData(jMessageId, getSubscribedData())); }, 0, UPDATE_INTERVAL_IN_SECONDS, TimeUnit.SECONDS); } diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 3db36d2a042..aeda9de7f51 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -3,14 +3,18 @@ import java.util.HashMap; import java.util.Map.Entry; import java.util.Optional; +import java.util.UUID; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; import io.openems.common.types.FieldValue; import io.openems.common.types.NumberFieldValue; import io.openems.common.types.StringFieldValue; +import io.openems.common.utils.JsonUtils; public class DefaultMessages { @@ -23,6 +27,7 @@ public class DefaultMessages { * } * } * + * * @param jMessageId * @return */ @@ -31,7 +36,7 @@ private static JsonObject newMessage(JsonObject jMessageId) { j.add("messageId", jMessageId); return j; } - + /** *
         	 *	{
        @@ -329,7 +334,7 @@ public static JsonObject logUnsubscribe(JsonObject jMessageId) {
         		j.add("log", jLog);
         		return j;
         	}
        -	
        +
         	/**
         	 * 
         	 *	{
        @@ -351,4 +356,32 @@ public static JsonObject systemExecuteReply(JsonObject jMessageId, String output
         		j.add("system", jSystem);
         		return j;
         	}
        +
        +	/**
        +	 * Adds the backend identifier to messageId. Used for forwarding UI-messages to
        +	 * Edge
        +	 * 
        +	 * @param jMessage
        +	 * @param uuid
        +	 * @return
        +	 * @throws OpenemsException
        +	 */
        +	public static JsonObject prepareMessageForForwardToEdge(JsonObject jMessage, UUID uuid, Optional roleOpt)
        +			throws OpenemsException {
        +		JsonObject jMessageId = JsonUtils.getAsJsonObject(jMessage, "messageId");
        +		jMessageId.addProperty("backend", uuid.toString());
        +		jMessage.add("messageId", jMessageId);
        +		jMessage.remove("edgeId");
        +		if (roleOpt.isPresent()) {
        +			jMessage.addProperty("role", roleOpt.get().toString().toLowerCase());
        +		}
        +		return jMessage;
        +	}
        +	
        +	public static JsonObject prepareMessageForForwardToUi(JsonObject jMessage) throws OpenemsException {
        +		JsonObject jMessageId = JsonUtils.getAsJsonObject(jMessage, "messageId");
        +		jMessageId.remove("backend");
        +		jMessage.add("messageId", jMessageId);
        +		return jMessage;
        +	}
         }
        diff --git a/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java
        index ce07ca1b057..22fa0613b60 100644
        --- a/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java
        +++ b/io.openems.common/src/io/openems/common/websocket/WebSocketUtils.java
        @@ -7,44 +7,51 @@
         import org.slf4j.Logger;
         import org.slf4j.LoggerFactory;
         
        -import com.google.gson.JsonArray;
         import com.google.gson.JsonObject;
         
        +import io.openems.common.exceptions.OpenemsException;
         import io.openems.common.utils.StringUtils;
         
         public class WebSocketUtils {
         
         	private static Logger log = LoggerFactory.getLogger(WebSocketUtils.class);
         
        -	public static boolean send(Optional websocketOpt, JsonObject j) {
        +	public static void sendOrLogError(Optional websocketOpt, JsonObject j) {
         		if (!websocketOpt.isPresent()) {
         			log.error("Websocket is not available. Unable to send [" + StringUtils.toShortString(j, 100) + "]");
        -			return false;
         		} else {
        -			return WebSocketUtils.send(websocketOpt.get(), j);
        +			sendOrLogError(websocketOpt.get(), j);
         		}
         	}
         
        -	public static boolean sendNotification(Optional websocketOpt, JsonArray jId, LogBehaviour logBehaviour,
        -			Notification code, Object... params) {
        +	public static void sendNotificationOrLogError(Optional websocketOpt, JsonObject jMessageId,
        +			LogBehaviour logBehaviour, Notification code, Object... params) {
         		if (!websocketOpt.isPresent()) {
         			log.error("Websocket is not available. Unable to send Notification ["
         					+ String.format(code.getMessage(), params) + "]");
        -			return false;
         		} else {
        -			return WebSocketUtils.sendNotification(websocketOpt.get(), jId, logBehaviour, code, params);
        +			WebSocketUtils.sendNotificationOrLogError(websocketOpt.get(), jMessageId, logBehaviour, code, params);
         		}
         	}
         
        -	public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBehaviour logBehaviour,
        +	public static void sendNotificationOrLogError(WebSocket websocket, JsonObject jMessageId, LogBehaviour logBehaviour,
         			Notification notification, Object... params) {
         		if (logBehaviour.equals(LogBehaviour.WRITE_TO_LOG)) {
         			// log message
         			notification.writeToLog(log, params);
         		}
         		String message = String.format(notification.getMessage(), params);
        -		JsonObject j = DefaultMessages.notification(new JsonObject() /* TODO */, notification, message, params);
        -		return WebSocketUtils.send(websocket, j);
        +		JsonObject j = DefaultMessages.notification(jMessageId, notification, message, params);
        +		WebSocketUtils.sendOrLogError(websocket, j);
        +	}
        +
        +	public static void send(Optional websocketOpt, JsonObject j) throws OpenemsException {
        +		if (!websocketOpt.isPresent()) {
        +			throw new OpenemsException(
        +					"Websocket is not available. Unable to send [" + StringUtils.toShortString(j, 100) + "]");
        +		} else {
        +			send(websocketOpt.get(), j);
        +		}
         	}
         
         	/**
        @@ -53,14 +60,25 @@ public static boolean sendNotification(WebSocket websocket, JsonArray jId, LogBe
         	 * @param j
         	 * @return true if successful, otherwise false
         	 */
        -	public static boolean send(WebSocket websocket, JsonObject j) {
        -		// System.out.println("SEND: websocket["+websocket+"]: " + j.toString());
        +	public static void send(WebSocket websocket, JsonObject j) throws OpenemsException {
         		try {
         			websocket.send(j.toString());
        -			return true;
         		} catch (WebsocketNotConnectedException e) {
        -			log.error("Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]");
        -			return false;
        +			throw new OpenemsException(
        +					"Websocket is not connected. Unable to send [" + StringUtils.toShortString(j, 100) + "]");
        +		}
        +	}
        +
        +	/**
        +	 * Send a message to a websocket. If sending fails, it is logged as an error
        +	 *
        +	 * @param j
        +	 */
        +	public static void sendOrLogError(WebSocket websocket, JsonObject j) {
        +		try {
        +			WebSocketUtils.send(websocket, j);
        +		} catch (OpenemsException e) {
        +			log.error(e.getMessage());
         		}
         	}
         }
        diff --git a/ui/src/app/device/config/more/directcontrol/directcontrol.component.ts b/ui/src/app/device/config/more/directcontrol/directcontrol.component.ts
        index ef9b7085d83..06546a6f27e 100644
        --- a/ui/src/app/device/config/more/directcontrol/directcontrol.component.ts
        +++ b/ui/src/app/device/config/more/directcontrol/directcontrol.component.ts
        @@ -39,7 +39,7 @@ export class DirectControlComponent {
               let thing = form.value["thing"];
               let channel = form.value["channel"];
               let value = form.value["value"];
        -      this.device.send(DefaultMessages.configUpdate(thing, channel, value));
        +      this.device.send(DefaultMessages.configUpdate(this.device.edgeId, thing, channel, value));
             }
           }
         
        diff --git a/ui/src/app/shared/config/channel.component.ts b/ui/src/app/shared/config/channel.component.ts
        index 63eed8b8495..5b7c27fd12a 100644
        --- a/ui/src/app/shared/config/channel.component.ts
        +++ b/ui/src/app/shared/config/channel.component.ts
        @@ -139,12 +139,12 @@ export class ChannelComponent implements OnChanges, OnDestroy {
             if (this.isJson) {
               try {
                 value = JSON.parse(value);
        -        this.message.next(DefaultMessages.configUpdate(this.thingId, this.channelId, value));
        +        this.message.next(DefaultMessages.configUpdate(this.device.edgeId, this.thingId, this.channelId, value));
               } catch (e) {
                 this.message.next(null);
               }
             } else {
        -      this.message.next(DefaultMessages.configUpdate(this.thingId, this.channelId, value));
        +      this.message.next(DefaultMessages.configUpdate(this.device.edgeId, this.thingId, this.channelId, value));
             }
         
           }
        diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts
        index 1725c5b7687..1c871b63ab1 100644
        --- a/ui/src/app/shared/service/defaultmessages.ts
        +++ b/ui/src/app/shared/service/defaultmessages.ts
        @@ -37,11 +37,12 @@ export class DefaultMessages {
                 }
             };
         
        -    public static configUpdate(thingId: string, channelId: string, value: any): DefaultTypes.ConfigUpdate {
        +    public static configUpdate(edgeId: number, thingId: string, channelId: string, value: any): DefaultTypes.ConfigUpdate {
                 return {
                     messageId: {
                         ui: UUID.UUID()
                     },
        +            edgeId: edgeId,
                     config: {
                         mode: "update",
                         thing: thingId,
        
        From 0fe2334792bf668ebdb772e7875e313107eabab1 Mon Sep 17 00:00:00 2001
        From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= 
        Date: Mon, 26 Feb 2018 10:49:08 +0100
        Subject: [PATCH 104/156] Add javadoc
        
        ---
         .../core/utilities/power/SymmetricPower.java  | 90 +++++++++++++++++--
         1 file changed, 81 insertions(+), 9 deletions(-)
        
        diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPower.java b/edge/src/io/openems/core/utilities/power/SymmetricPower.java
        index 5cc8e72f560..de32ec731c4 100644
        --- a/edge/src/io/openems/core/utilities/power/SymmetricPower.java
        +++ b/edge/src/io/openems/core/utilities/power/SymmetricPower.java
        @@ -29,14 +29,29 @@ public abstract class SymmetricPower {
         			Color.ORANGE, Color.RED };
         	protected static final Coordinate ZERO = new Coordinate(0, 0);
         
        +	/**
        +	 * Returns the GeometryFactory used for the Polygon creation
        +	 * @return
        +	 */
         	public static GeometryFactory getFactory() {
         		return FACTORY;
         	}
        -
        +	/**
        +	 * Returns the GeometricShapeFactory used for the creation of Circles
        +	 * @return
        +	 */
         	public static GeometricShapeFactory getShapefactory() {
         		return SHAPEFACTORY;
         	}
        -
        +	/**
        +	 * Creates a Rect with the coordinates pMin, pMax, qMin, qMax and intersects the rect with the base geometry.
        +	 * @param base
        +	 * @param pMin
        +	 * @param pMax
        +	 * @param qMin
        +	 * @param qMax
        +	 * @return resulting polygon after the intersection
        +	 */
         	public static Geometry intersectRect(Geometry base, double pMin, double pMax, double qMin, double qMax) {
         		Coordinate[] coordinates = new Coordinate[] { new Coordinate(pMin, qMax), new Coordinate(pMin, qMin),
         				new Coordinate(pMax, qMin), new Coordinate(pMax, qMax), new Coordinate(pMin, qMax) };
        @@ -67,11 +82,17 @@ public SymmetricPower() {
         	/*
         	 * Methods
         	 */
        -
        +	/**
        +	 * Returns the maximal possible ApparentPower
        +	 * @return
        +	 */
         	public long getMaxApparentPower() {
         		return maxApparentPower;
         	}
        -
        +	/**
        +	 * set the maximal possible ApparentPower
        +	 * @param power
        +	 */
         	protected void setMaxApparentPower(long power) {
         		this.maxApparentPower = Math.abs(power);
         	}
        @@ -79,7 +100,10 @@ protected void setMaxApparentPower(long power) {
         	public Geometry getGeometry() {
         		return this.geometry;
         	}
        -
        +	/**
        +	 * updates the geometrie, calculates the min and max power and notifies all powerChangedListener
        +	 * @param g
        +	 */
         	protected void setGeometry(Geometry g) {
         		this.geometry = g;
         		this.geometries.add(g);
        @@ -90,7 +114,9 @@ protected void setGeometry(Geometry g) {
         			}
         		}
         	}
        -
        +	/**
        +	 * Calculates the min and max active and reactivePower possible in the geometry.
        +	 */
         	private void calculateMinMax() {
         		this.maxP = Optional.ofNullable(getClosestP(maxApparentPower));
         		this.minP = Optional.ofNullable(getClosestP(maxApparentPower * -1));
        @@ -98,6 +124,11 @@ private void calculateMinMax() {
         		this.minQ = Optional.ofNullable(getClosestQ(maxApparentPower * -1));
         	}
         
        +	/**
        +	 * Calculates the colses activepower point according to the parameter p
        +	 * @param p
        +	 * @return
        +	 */
         	private Long getClosestP(long p) {
         		Coordinate[] coordinates = new Coordinate[] { new Coordinate(p, maxApparentPower),
         				new Coordinate(p, maxApparentPower * -1) };
        @@ -111,7 +142,11 @@ private Long getClosestP(long p) {
         		}
         		return null;
         	}
        -
        +	/**
        +	 * Calculates the colses reactivepower point according to the parameter q
        +	 * @param p
        +	 * @return
        +	 */
         	private Long getClosestQ(long q) {
         		Coordinate[] coordinates = new Coordinate[] { new Coordinate(maxApparentPower, q),
         				new Coordinate(maxApparentPower * -1, q) };
        @@ -125,47 +160,84 @@ private Long getClosestQ(long q) {
         		}
         		return null;
         	}
        -
        +	/**
        +	 * Add PowerResetListener
        +	 * @param listener
        +	 */
         	public void addListener(PowerResetListener listener) {
         		this.resetListeners.add(listener);
         	}
         
        +	/**
        +	 * Remove PowerResetListener
        +	 * @param listener
        +	 */
         	public void removeListener(PowerResetListener listener) {
         		this.resetListeners.add(listener);
         	}
         
        +	/**
        +	 * Add PowerChangeListener
        +	 * @param listener
        +	 */
         	public void addListener(PowerChangeListener listener) {
         		synchronized (this.changeListeners) {
         			this.changeListeners.add(listener);
         		}
         	}
         
        +	/**
        +	 * Remove PowerChangeListener
        +	 * @param listener
        +	 */
         	public void removeListener(PowerChangeListener listener) {
         		synchronized (this.changeListeners) {
         			this.changeListeners.add(listener);
         		}
         	}
         
        +	/**
        +	 * Allyies a limit to the current power representing polygon.
        +	 * @param limit the Limitation implementation to apply
        +	 * @throws PowerException this Exception is thrown if the result is empty
        +	 */
         	public abstract void applyLimitation(Limitation limit) throws PowerException;
         
        +	/**
        +	 * Returns the max activepower, after all limitations applied.
        +	 * @return
        +	 */
         	public Optional getMaxP() {
         		return this.maxP;
         	}
         
        +	/**
        +	 * Returns the min activepower, after all limitations applied.
        +	 * @return
        +	 */
         	public Optional getMinP() {
         		return this.minP;
         	}
         
        +	/**
        +	 * Returns the max reactivepower, after all limitations applied.
        +	 * @return
        +	 */
         	public Optional getMaxQ() {
         		return this.maxQ;
         	}
         
        +	/**
        +	 * Returns the min reactivepower, after all limitations applied.
        +	 * @return
        +	 */
         	public Optional getMinQ() {
         		return this.minQ;
         	}
         
         	protected void reset() {
         		this.geometries.clear();
        +		this.geometries.add(getGeometry());
         		for (PowerResetListener listener : this.resetListeners) {
         			Geometry g = listener.afterPowerReset(getGeometry());
         			if (!g.isEmpty()) {
        @@ -201,7 +273,7 @@ public String getAsSVG() {
         		text.append("\n");
         		text.append(
         				"\n");
        -		text.append("\n");
         		int i = 0;
         		for (Geometry geo : geometries) {
        
        From b50019be480cb60135580b8b2f08d424af17fee1 Mon Sep 17 00:00:00 2001
        From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= 
        Date: Mon, 26 Feb 2018 14:14:00 +0100
        Subject: [PATCH 105/156] add time to stop surplus and fix no charge of acPV
        
        ---
         .../BalancingSurplusController.java           | 25 +++++++++++++++----
         1 file changed, 20 insertions(+), 5 deletions(-)
        
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        index a57fda39acc..1c03e9bb0ba 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        @@ -20,6 +20,8 @@
          *******************************************************************************/
         package io.openems.impl.controller.symmetric.balancingsurplus;
         
        +import java.time.LocalTime;
        +import java.time.format.DateTimeParseException;
         import java.util.Set;
         
         import io.openems.api.channel.ConfigChannel;
        @@ -31,7 +33,7 @@
         import io.openems.core.utilities.power.PowerException;
         
         @ThingInfo(title = "Self-consumption optimization with surplus feed-in (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. If ess is over the surplusMinSoc, the ess discharges with the power of the chargers. ")
        -public class BalancingSurplusController extends Controller {
        +public class BalancingSurplusController extends Controller{
         
         	private ThingStateChannels thingState = new ThingStateChannels(this);
         	/*
        @@ -60,6 +62,9 @@ public BalancingSurplusController(String thingId) {
         	@ChannelInfo(title = "Grid-Meter", description = "Sets the grid meter.", type = Meter.class)
         	public final ConfigChannel meter = new ConfigChannel("meter", this);
         
        +	@ChannelInfo(title = "Surplus Off Time", description = "The time to stop grid feed in.", type = String.class, defaultValue = "17:00:00", isOptional = true)
        +	public final ConfigChannel surplusOffTime = new ConfigChannel("surplusOffTime", this);
        +
         	private long surplus = 0L;
         	private boolean surplusOn = false;
         
        @@ -73,8 +78,10 @@ public void run() {
         			Ess ess = this.ess.value();
         			// Calculate required sum values
         			long calculatedPower = meter.value().activePower.value() + ess.activePower.value();
        -			surplus = getSurplusPower()- calculatedPower;
        -			if (surplus < 0) {
        +			surplus = getSurplusPower();
        +			if (surplus > 0) {
        +				surplus -= calculatedPower;
        +			}else {
         				surplus = 0l;
         			}
         			calculatedPower += surplus;
        @@ -89,9 +96,9 @@ public void run() {
         
         	private long getSurplusPower() throws InvalidValueException {
         		long power = 0l;
        -		if (ess.value().allowedCharge.value() >= -100 && getPvVoltage() >= 250000) {
        +		if (ess.value().allowedCharge.value() >= -100 && LocalTime.now().isBefore(getSurplusStopTime()) && getPvVoltage() >= 250000) {
         			surplusOn = true;
        -		} else if (ess.value().soc.value() < surplusMinSoc.value() || getPvVoltage() < 200000) {
        +		} else if (ess.value().soc.value() < surplusMinSoc.value() || LocalTime.now().isAfter(getSurplusStopTime())) {
         			surplusOn = false;
         		}
         		if (surplusOn) {
        @@ -121,4 +128,12 @@ public ThingStateChannels getStateChannel() {
         		return this.thingState;
         	}
         
        +	private LocalTime getSurplusStopTime() {
        +		try {
        +			return LocalTime.parse(surplusOffTime.valueOptional().orElse("17:00"));
        +		}catch (DateTimeParseException e) {
        +			return LocalTime.of(17, 0);
        +		}
        +	}
        +
         }
        
        From 69154211e2342a102ea71cd252568e3ce5710bc2 Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Tue, 27 Feb 2018 11:02:54 +0100
        Subject: [PATCH 106/156] Subscribe to Log
        
        ---
         .../websocket/EdgeWebsocketHandler.java       | 77 +++++++--------
         .../websocket/WebsocketLogAppender.java       | 78 +++++++--------
         .../org/slf4j/impl/StaticLoggerBinder.java    | 95 +++++++++++++++++++
         .../impl/provider/EdgeWebsocket.java          |  2 +-
         .../impl/provider/UiWebsocketServer.java      |  1 +
         ui/src/app/device/config/log/log.component.ts |  5 +-
         ui/src/app/shared/device/device.ts            |  7 +-
         ui/src/app/shared/service/defaultmessages.ts  | 16 ++--
         8 files changed, 188 insertions(+), 93 deletions(-)
         create mode 100644 edge/src/org/slf4j/impl/StaticLoggerBinder.java
        
        diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        index b3383b136f1..663b7d8139f 100644
        --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        @@ -21,11 +21,10 @@
         package io.openems.core.utilities.websocket;
         
         import java.util.HashMap;
        -import java.util.HashSet;
        +import java.util.Map;
         import java.util.Map.Entry;
         import java.util.NoSuchElementException;
         import java.util.Optional;
        -import java.util.Set;
         import java.util.concurrent.ExecutorService;
         import java.util.concurrent.Executors;
         
        @@ -81,9 +80,9 @@ public class EdgeWebsocketHandler {
         	private final HashMap currentDataSubscribers = new HashMap<>();
         
         	/**
        -	 * Holds subscribers to system log
        +	 * Holds subscribers to system log (identified by messageId.backend, holds complete jMessageId)
         	 */
        -	private final Set logSubscribers = new HashSet<>();
        +	private final Map logSubscribers = new HashMap<>();
         
         	/**
         	 * Executor for system log task
        @@ -168,8 +167,6 @@ public final void onMessage(JsonObject jMessage) {
         			/*
         			 * Query historic data
         			 */
        -			// Optional jMessageIdOpt, String deviceName, WebSocket websocket, JsonElement
        -			// jHistoricDataElement
         			Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData");
         			if (jhistoricDataOpt.isPresent()) {
         				// select first QueryablePersistence (by default the running InfluxdbPersistence)
        @@ -194,15 +191,14 @@ public final void onMessage(JsonObject jMessage) {
         			/*
         			 * Subscribe to log
         			 */
        -			// TODO
        -			// Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log");
        -			// if (jLogOpt.isPresent() && jIdOpt.isPresent()) {
        -			// try {
        -			// jReply = JsonUtils.merge(jReply, //
        -			// log(jIdOpt.get(), jLogOpt.get(), role) //
        -			// );
        -			// } catch (AccessDeniedException e) { /* ignore */ }
        -			// }
        +			Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log");
        +			if (jLogOpt.isPresent()) {
        +				try {
        +					log(jMessageId, jLogOpt.get(), role);
        +				} catch (OpenemsException e) {
        +					log.error(e.getMessage());
        +				}
        +			}
         
         			/*
         			 * Remote system control
        @@ -361,29 +357,26 @@ private synchronized JsonObject currentData(Role role, JsonObject jMessageId, Js
         	 * @param j
         	 * @throws AccessDeniedException
         	 */
        -	private synchronized JsonObject log(JsonArray jId, JsonObject jLog, Role role) throws AccessDeniedException {
        +	private synchronized void log(JsonObject jMessageId, JsonObject jLog, Role role) throws OpenemsException {
         		if (!(role == Role.ADMIN || role == Role.INSTALLER || role == Role.OWNER)) {
         			throw new AccessDeniedException("User role [" + role + "] is not allowed to read system logs.");
         		}
        -		try {
        -			String mode = JsonUtils.getAsString(jLog, "mode");
        -			String messageId = jId.get(jId.size() - 1).getAsString();
        +		String mode = JsonUtils.getAsString(jLog, "mode");
        +		String messageIdBackend = JsonUtils.getAsString(jMessageId, "backend");
         
        -			if (mode.equals("subscribe")) {
        -				/*
        -				 * Subscribe to system log
        -				 */
        -				this.logSubscribers.add(messageId);
        -			} else if (mode.equals("unsubscribe")) {
        -				/*
        -				 * Unsubscribe from system log
        -				 */
        -				this.logSubscribers.remove(messageId);
        -			}
        -		} catch (OpenemsException e) {
        -			log.warn(e.getMessage());
        +		if (mode.equals("subscribe")) {
        +			/*
        +			 * Subscribe to system log
        +			 */
        +			log.info("UI [" + messageIdBackend + "] subscribed to log...");
        +			this.logSubscribers.put(messageIdBackend, jMessageId);
        +		} else if (mode.equals("unsubscribe")) {
        +			/*
        +			 * Unsubscribe from system log
        +			 */
        +			log.info("UI [" + messageIdBackend + "] unsubscribed from log...");
        +			this.logSubscribers.remove(messageIdBackend);
         		}
        -		return new JsonObject();
         	}
         
         	/**
        @@ -674,15 +667,15 @@ public void sendLog(long timestamp, String level, String source, String message)
         			// nobody subscribed
         			return;
         		}
        -		for (String id : this.logSubscribers) {
        -			JsonArray jId = new JsonArray();
        -			jId.add("log");
        -			jId.add(id);
        -			JsonObject j = DefaultMessages.log(new JsonObject() /* TODO */, timestamp, level, source, message);
        -			// TODO reevaluate if it is necessary to do this async; ie if websocket.send returns directly or not
        -			logExecutor.execute(() -> {
        -				this.sendOrLogError(j);
        -			});
        +		for (Entry entry : this.logSubscribers.entrySet()) {
        +			JsonObject j = DefaultMessages.log(entry.getValue(), timestamp, level, source, message);
        +			try {
        +				this.send(j);
        +			} catch (OpenemsException e) {
        +				// Error while sending: remove subscriber
        +				log.error("Error while sending log. Removing subscriber [" + entry.getKey() + "]");
        +				this.logSubscribers.remove(entry.getKey());
        +			}
         		}
         	}
         
        diff --git a/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java b/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java
        index 7ed594a2edb..73692dcc495 100644
        --- a/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java
        +++ b/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java
        @@ -1,39 +1,39 @@
        -package io.openems.core.utilities.websocket;
        -
        -import ch.qos.logback.classic.spi.ILoggingEvent;
        -import ch.qos.logback.core.AppenderBase;
        -import io.openems.api.controller.Controller;
        -import io.openems.api.scheduler.Scheduler;
        -import io.openems.core.ThingRepository;
        -import io.openems.impl.controller.api.websocket.WebsocketApiController;
        -import io.openems.impl.persistence.fenecon.FeneconPersistence;
        -
        -public class WebsocketLogAppender extends AppenderBase {
        -
        -	@Override
        -	protected void append(ILoggingEvent event) {
        -		long timestamp = event.getTimeStamp();
        -		String level = event.getLevel().toString();
        -		String source = event.getLoggerName();
        -		String message = event.getFormattedMessage();
        -
        -		ThingRepository thingRepository = ThingRepository.getInstance();
        -		for (Scheduler scheduler : thingRepository.getSchedulers()) {
        -			for (Controller controller : scheduler.getControllers()) {
        -				if (controller instanceof WebsocketApiController) {
        -					WebsocketApiController websocketApiController = (WebsocketApiController) controller;
        -					websocketApiController.broadcastLog(timestamp, level, source, message);
        -				}
        -			}
        -		}
        -
        -		// send to fenecon persistence
        -		ThingRepository.getInstance().getPersistences().forEach((persistence) -> {
        -			if (persistence instanceof FeneconPersistence) {
        -				FeneconPersistence p = (FeneconPersistence) persistence;
        -				p.getWebsocketHandler().sendLog(timestamp, level, source, message);
        -			}
        -		});
        -	}
        -
        -}
        +package io.openems.core.utilities.websocket;
        +
        +import ch.qos.logback.classic.spi.ILoggingEvent;
        +import ch.qos.logback.core.AppenderBase;
        +import io.openems.api.controller.Controller;
        +import io.openems.api.scheduler.Scheduler;
        +import io.openems.core.ThingRepository;
        +import io.openems.impl.controller.api.websocket.WebsocketApiController;
        +import io.openems.impl.persistence.fenecon.FeneconPersistence;
        +
        +public class WebsocketLogAppender extends AppenderBase {
        +
        +	@Override
        +	protected void append(ILoggingEvent event) {
        +		long timestamp = event.getTimeStamp();
        +		String level = event.getLevel().toString();
        +		String source = event.getLoggerName();
        +		String message = event.getFormattedMessage();
        +
        +		ThingRepository thingRepository = ThingRepository.getInstance();
        +		for (Scheduler scheduler : thingRepository.getSchedulers()) {
        +			for (Controller controller : scheduler.getControllers()) {
        +				if (controller instanceof WebsocketApiController) {
        +					WebsocketApiController websocketApiController = (WebsocketApiController) controller;
        +					websocketApiController.broadcastLog(timestamp, level, source, message);
        +				}
        +			}
        +		}
        +
        +		// send to fenecon persistence
        +		ThingRepository.getInstance().getPersistences().forEach((persistence) -> {
        +			if (persistence instanceof FeneconPersistence) {
        +				FeneconPersistence p = (FeneconPersistence) persistence;
        +				p.getWebsocketHandler().sendLog(timestamp, level, source, message);
        +			}
        +		});
        +	}
        +
        +}
        diff --git a/edge/src/org/slf4j/impl/StaticLoggerBinder.java b/edge/src/org/slf4j/impl/StaticLoggerBinder.java
        new file mode 100644
        index 00000000000..d6c18ccbad2
        --- /dev/null
        +++ b/edge/src/org/slf4j/impl/StaticLoggerBinder.java
        @@ -0,0 +1,95 @@
        +package org.slf4j.impl;
        +
        +import org.slf4j.ILoggerFactory;
        +import org.slf4j.helpers.Util;
        +import org.slf4j.spi.LoggerFactoryBinder;
        +
        +import ch.qos.logback.classic.LoggerContext;
        +import ch.qos.logback.classic.util.ContextInitializer;
        +import ch.qos.logback.classic.util.ContextSelectorStaticBinder;
        +import ch.qos.logback.core.CoreConstants;
        +import ch.qos.logback.core.joran.spi.JoranException;
        +import ch.qos.logback.core.status.StatusUtil;
        +import ch.qos.logback.core.util.StatusPrinter;
        +
        +public class StaticLoggerBinder implements LoggerFactoryBinder {
        +
        +	/**
        +	 * Declare the version of the SLF4J API this implementation is compiled
        +	 * against. The value of this field is usually modified with each release.
        +	 */
        +	// to avoid constant folding by the compiler, this field must *not* be final
        +	public static String REQUESTED_API_VERSION = "1.7.16"; // !final
        +
        +	final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS";
        +
        +	/**
        +	 * The unique instance of this class.
        +	 */
        +	private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
        +
        +	private static Object KEY = new Object();
        +
        +	static {
        +		SINGLETON.init();
        +	}
        +
        +	private boolean initialized = false;
        +	private LoggerContext defaultLoggerContext = new LoggerContext();
        +	private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
        +
        +	private StaticLoggerBinder() {
        +		defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
        +	}
        +
        +	public static StaticLoggerBinder getSingleton() {
        +		return SINGLETON;
        +	}
        +
        +	/**
        +	 * Package access for testing purposes.
        +	 */
        +	static void reset() {
        +		SINGLETON = new StaticLoggerBinder();
        +		SINGLETON.init();
        +	}
        +
        +	/**
        +	 * Package access for testing purposes.
        +	 */
        +	void init() {
        +		try {
        +			try {
        +				new ContextInitializer(defaultLoggerContext).autoConfig();
        +			} catch (JoranException je) {
        +				Util.report("Failed to auto configure default logger context", je);
        +			}
        +			// logback-292
        +			if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
        +				StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
        +			}
        +			contextSelectorBinder.init(defaultLoggerContext, KEY);
        +			initialized = true;
        +		} catch (Exception t) { // see LOGBACK-1159
        +			Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
        +		}
        +	}
        +
        +	@Override
        +	public ILoggerFactory getLoggerFactory() {
        +		if (!initialized) {
        +			return defaultLoggerContext;
        +		}
        +
        +		if (contextSelectorBinder.getContextSelector() == null) {
        +			throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
        +		}
        +		return contextSelectorBinder.getContextSelector().getLoggerContext();
        +	}
        +
        +	@Override
        +	public String getLoggerFactoryClassStr() {
        +		return contextSelectorBinder.getClass().getName();
        +	}
        +
        +}
        \ No newline at end of file
        diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java
        index 9416c46a1ab..2cb498a9d16 100644
        --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java
        +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java
        @@ -25,7 +25,7 @@
         import org.osgi.service.metatype.annotations.ObjectClassDefinition;
         
         @Designate(ocd = EdgeWebsocket.Config.class, factory = false)
        -@Component(name = "EdgeWebsocket", immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE)
        +@Component(name = "EdgeWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE)
         public class EdgeWebsocket implements EdgeWebsocketService {
         
         	private final Logger log = LoggerFactory.getLogger(EdgeWebsocket.class);
        diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java
        index 7b5a9944c78..d441980c6ab 100644
        --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java
        +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java
        @@ -203,6 +203,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) {
         			 */
         			if (jMessage.has("config") || jMessage.has("log") || jMessage.has("system")) {
         				try {
        +					log.info("User [" + user.getName() + "] Forward message to Edge [" + edge.getName() +"]: " + StringUtils.toShortString(jMessage, 100));
         					Optional roleOpt = user.getEdgeRole(edgeId);
         					JsonObject j = DefaultMessages.prepareMessageForForwardToEdge(jMessage, data.getUuid(), roleOpt);
         					this.parent.edgeWebsocketService.forwardMessageFromUi(edgeId, j);
        diff --git a/ui/src/app/device/config/log/log.component.ts b/ui/src/app/device/config/log/log.component.ts
        index 23369f45720..f3551dfe9ad 100644
        --- a/ui/src/app/device/config/log/log.component.ts
        +++ b/ui/src/app/device/config/log/log.component.ts
        @@ -18,6 +18,8 @@ export class LogComponent implements OnInit, OnDestroy {
           public logs: DefaultTypes.Log[] = [];
           public isSubscribed: boolean = false;
         
        +  private subscription: Subscription;
        +
           private MAX_LOG_ENTRIES = 200;
           private stopOnDestroy: Subject = new Subject();
         
        @@ -56,7 +58,7 @@ export class LogComponent implements OnInit, OnDestroy {
             }
         
             if (this.device != null) {
        -      this.device.subscribeLog().takeUntil(this.stopOnDestroy).subscribe(log => {
        +      this.subscription = this.device.subscribeLog().takeUntil(this.stopOnDestroy).subscribe(log => {
                 log.time = format(new Date(log.time * 1000), "DD.MM.YYYY HH:mm:ss");
                 switch (log.level) {
                   case 'INFO':
        @@ -82,6 +84,7 @@ export class LogComponent implements OnInit, OnDestroy {
           }
         
           public unsubscribeLog() {
        +    this.subscription.unsubscribe();
             if (this.device != null) {
               this.device.unsubscribeLog();
             }
        diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts
        index f97249bfe38..9cc154dc2b6 100644
        --- a/ui/src/app/shared/device/device.ts
        +++ b/ui/src/app/shared/device/device.ts
        @@ -155,16 +155,15 @@ export class Device {
            * Subscribe to log
            */
           public subscribeLog(): Observable {
        -    let message = DefaultMessages.logSubscribe();
        -    this.send(message);
        -    return this.log;
        +    let replyStream = this.sendMessageWithReply(DefaultMessages.logSubscribe(this.edgeId));
        +    return replyStream.map(message => message.log as DefaultTypes.Log);
           }
         
           /**
            * Unsubscribe from log
            */
           public unsubscribeLog() {
        -    let message = DefaultMessages.logUnsubscribe();
        +    let message = DefaultMessages.logUnsubscribe(this.edgeId);
             this.send(message);
           }
         
        diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts
        index 1c871b63ab1..059e9608088 100644
        --- a/ui/src/app/shared/service/defaultmessages.ts
        +++ b/ui/src/app/shared/service/defaultmessages.ts
        @@ -85,20 +85,24 @@ export class DefaultMessages {
                 }
             };
         
        -    public static logSubscribe() {
        +    public static logSubscribe(edgeId: number): DefaultTypes.IdentifiedMessage {
                 return {
        -            device: String,
        -            id: ["log"],
        +            messageId: {
        +                ui: UUID.UUID()
        +            },
        +            edgeId: edgeId,
                     log: {
                         mode: "subscribe",
                     }
                 }
             };
         
        -    public static logUnsubscribe() {
        +    public static logUnsubscribe(edgeId: number): DefaultTypes.IdentifiedMessage {
                 return {
        -            device: String,
        -            id: ["log"],
        +            messageId: {
        +                ui: UUID.UUID()
        +            },
        +            edgeId: edgeId,
                     log: {
                         mode: "unsubscribe",
                     }
        
        From 82699ad191189315f92ff234216702140737200a Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Tue, 27 Feb 2018 11:56:23 +0100
        Subject: [PATCH 107/156] Reimplement system command
        
        ---
         .../websocket/EdgeWebsocketHandler.java       | 87 ++++++++-----------
         ui/src/app/device/config/log/log.component.ts |  5 +-
         ui/src/app/shared/device/device.ts            | 42 ++-------
         ui/src/app/shared/service/defaultmessages.ts  |  8 +-
         4 files changed, 49 insertions(+), 93 deletions(-)
        
        diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        index 663b7d8139f..32766658dae 100644
        --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        @@ -25,8 +25,6 @@
         import java.util.Map.Entry;
         import java.util.NoSuchElementException;
         import java.util.Optional;
        -import java.util.concurrent.ExecutorService;
        -import java.util.concurrent.Executors;
         
         import org.java_websocket.WebSocket;
         import org.slf4j.Logger;
        @@ -84,11 +82,6 @@ public class EdgeWebsocketHandler {
         	 */
         	private final Map logSubscribers = new HashMap<>();
         
        -	/**
        -	 * Executor for system log task
        -	 */
        -	private final ExecutorService logExecutor = Executors.newCachedThreadPool();
        -
         	/**
         	 * Predefined role for this connection. If empty, role is taken from message (in onMessage method).
         	 */
        @@ -141,9 +134,6 @@ public final void onMessage(JsonObject jMessage) {
         			}
         		}
         
        -		// init edgeId as empty. It's only needed in backend
        -		Optional edgeIdOpt = Optional.empty();
        -
         		if (jMessageIdOpt.isPresent()) {
         			JsonObject jMessageId = jMessageIdOpt.get();
         			/*
        @@ -203,20 +193,15 @@ public final void onMessage(JsonObject jMessage) {
         			/*
         			 * Remote system control
         			 */
        -			// TODO
        -			// {
        -			// Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system");
        -			// if (jSystemOpt.isPresent() && jSystemOpt.isPresent()) {
        -			// try {
        -			// jReply = JsonUtils.merge(jReply, //
        -			// system(jIdOpt.get(), jSystemOpt.get(), role) //
        -			// );
        -			// } catch (AccessDeniedException e) {
        -			// // TODO create notification
        -			// log.error(e.getMessage());
        -			// }
        -			// }
        -			// }
        +			Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system");
        +			if (jSystemOpt.isPresent()) {
        +				try {
        +					system(jMessageId, jSystemOpt.get(), role);
        +				} catch (OpenemsException e) {
        +					// TODO create notification
        +					log.error(e.getMessage());
        +				}
        +			}
         		}
         	}
         
        @@ -237,7 +222,7 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional configChannel = (ConfigChannel) channel;
         						Object value = ConfigUtils.getConfigObject(configChannel, jValue);
         						configChannel.updateValue(value, true);
        -						WebSocketUtils.sendNotificationOrLogError(websocketOpt, jMessageId, LogBehaviour.WRITE_TO_LOG,
        -								Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue);
        +						WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, jMessageId,
        +								LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS,
        +								channel.address() + " => " + jValue);
         
         					} else if (channel instanceof WriteChannel) {
         						/*
        @@ -275,22 +261,25 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional writeChannel = (WriteChannel) channel;
         						if (!apiWorkerOpt.isPresent()) {
        -							WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */,
        +							WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, new JsonObject() /* TODO */,
         									LogBehaviour.WRITE_TO_LOG, Notification.BACKEND_NOT_ALLOWED,
         									"set " + channel.address() + " => " + jValue);
         						} else {
         							ApiWorker apiWorker = apiWorkerOpt.get();
         							WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> {
        -								WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */,
        -										LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS,
        +								WebSocketUtils.sendNotificationOrLogError(this.websocketOpt,
        +										new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG,
        +										Notification.EDGE_CHANNEL_UPDATE_SUCCESS,
         										"set " + channel.address() + " => " + jValue);
         							}).onFirstError((e) -> {
        -								WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */,
        -										LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_FAILED,
        +								WebSocketUtils.sendNotificationOrLogError(this.websocketOpt,
        +										new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG,
        +										Notification.EDGE_CHANNEL_UPDATE_FAILED,
         										"set " + channel.address() + " => " + jValue, e.getMessage());
         							}).onTimeout(() -> {
        -								WebSocketUtils.sendNotificationOrLogError(websocketOpt, new JsonObject() /* TODO */,
        -										LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_TIMEOUT,
        +								WebSocketUtils.sendNotificationOrLogError(this.websocketOpt,
        +										new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG,
        +										Notification.EDGE_CHANNEL_UPDATE_TIMEOUT,
         										"set " + channel.address() + " => " + jValue);
         							});
         							apiWorker.addValue(writeChannel, writeObject);
        @@ -300,7 +289,7 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional = new Subject();
         
        @@ -58,7 +56,7 @@ export class LogComponent implements OnInit, OnDestroy {
             }
         
             if (this.device != null) {
        -      this.subscription = this.device.subscribeLog().takeUntil(this.stopOnDestroy).subscribe(log => {
        +      this.device.subscribeLog().takeUntil(this.stopOnDestroy).subscribe(log => {
                 log.time = format(new Date(log.time * 1000), "DD.MM.YYYY HH:mm:ss");
                 switch (log.level) {
                   case 'INFO':
        @@ -84,7 +82,6 @@ export class LogComponent implements OnInit, OnDestroy {
           }
         
           public unsubscribeLog() {
        -    this.subscription.unsubscribe();
             if (this.device != null) {
               this.device.unsubscribeLog();
             }
        diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts
        index 9cc154dc2b6..4d353d3a38a 100644
        --- a/ui/src/app/shared/device/device.ts
        +++ b/ui/src/app/shared/device/device.ts
        @@ -70,6 +70,7 @@ export class Device {
            * Refresh the config
            */
           public refreshConfig(): BehaviorSubject {
        +    // TODO use sendMessageWithReply()
             let message = DefaultMessages.configQuery(this.edgeId);
             let messageId = message.messageId.ui;
             this.replyStreams[messageId] = new Subject();
        @@ -136,7 +137,7 @@ export class Device {
             // wait for reply
             return new Promise((resolve, reject) => {
               replyStream.first().subscribe(reply => {
        -        let historicData = (reply).historicData;
        +        let historicData = (reply as DefaultMessages.HistoricDataReply).historicData;
                 this.removeReplyStream(reply);
                 resolve(historicData);
               });
        @@ -171,45 +172,14 @@ export class Device {
            * System Execute
            */
           public systemExecute(password: string, command: string, background: boolean, timeout: number): Promise {
        -    let message = DefaultMessages.systemExecute(password, command, background, timeout);
        -    let messageId = message.id[0];
        -    this.replyStreams[messageId] = new Subject();
        -    this.send(message);
        +    let replyStream = this.sendMessageWithReply(DefaultMessages.systemExecute(this.edgeId, password, command, background, timeout));
             // wait for reply
             return new Promise((resolve, reject) => {
        -      this.replyStreams[messageId].first().subscribe(reply => {
        -        let output = (reply).system.output;
        -        this.replyStreams[messageId].unsubscribe();
        -        delete this.replyStreams[messageId];
        +      replyStream.first().subscribe(reply => {
        +        let output = (reply as DefaultMessages.SystemExecuteReply).system.output;
        +        this.removeReplyStream(reply);
                 resolve(output);
               });
             })
           }
        -
        -  /*
        -   * log
        -   */
        -  // if ("log" in message) {
        -  //   let log = message.log;
        -  //   this.log.next(log);
        -  // }
        -
        -
        -  //let kWh = null;
        -  // history data
        -  // if (message.queryreply != null) {
        -  //   if ("data" in message.queryreply && message.queryreply.data != null) {
        -  //     data = message.queryreply.data;
        -  //     for (let datum of data) {
        -  //       let sum = this.calculateSummary(datum.channels);
        -  //       datum["summary"] = sum;
        -  //     }
        -  //   }
        -  //   // kWh data
        -  //   if ("kWh" in message.queryreply) {
        -  //     kWh = message.queryreply.kWh;
        -  //   }
        -  // }
        -  //this.historykWh.next(kWh);
        -  // }
         }
        \ No newline at end of file
        diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts
        index 059e9608088..72319b0918d 100644
        --- a/ui/src/app/shared/service/defaultmessages.ts
        +++ b/ui/src/app/shared/service/defaultmessages.ts
        @@ -109,10 +109,12 @@ export class DefaultMessages {
                 }
             };
         
        -    public static systemExecute(password: string, command: string, background: boolean, timeout: number) {
        +    public static systemExecute(edgeId: number, password: string, command: string, background: boolean, timeout: number): DefaultTypes.IdentifiedMessage {
                 return {
        -            device: String,
        -            id: [UUID.UUID()],
        +            messageId: {
        +                ui: UUID.UUID()
        +            },
        +            edgeId: edgeId,
                     system: {
                         mode: "execute",
                         password: password,
        
        From 11df60717325dbfb39d847b8ebcdaebc947935e6 Mon Sep 17 00:00:00 2001
        From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= 
        Date: Tue, 27 Feb 2018 14:07:36 +0100
        Subject: [PATCH 108/156] fix no AC Charge possible
        
        ---
         .../symmetric/balancingsurplus/BalancingSurplusController.java  | 2 +-
         1 file changed, 1 insertion(+), 1 deletion(-)
        
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        index 1c03e9bb0ba..5b5a702d099 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        @@ -79,7 +79,7 @@ public void run() {
         			// Calculate required sum values
         			long calculatedPower = meter.value().activePower.value() + ess.activePower.value();
         			surplus = getSurplusPower();
        -			if (surplus > 0) {
        +			if (surplus > 0 && surplus > calculatedPower) {
         				surplus -= calculatedPower;
         			}else {
         				surplus = 0l;
        
        From 833fb7c95a006cfd8d7a21a715ffd3e787b9fdb4 Mon Sep 17 00:00:00 2001
        From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= 
        Date: Thu, 1 Mar 2018 14:57:05 +0100
        Subject: [PATCH 109/156] Refactor symmetricPower Package name
        
        ---
         .../api/device/nature/ess/SymmetricEssNature.java    |  2 +-
         .../CosPhiLineCharacteristicLimitation.java          |  2 +-
         .../power/{ => symmetric}/CosPhiLineLimitation.java  |  2 +-
         .../utilities/power/{ => symmetric}/Limitation.java  |  2 +-
         .../{ => symmetric}/LimitationChangedListener.java   |  2 +-
         .../power/{ => symmetric}/MaxCosPhiLimitation.java   |  2 +-
         .../power/{ => symmetric}/NoPBetweenLimitation.java  |  2 +-
         .../power/{ => symmetric}/NoQBetweenLimitation.java  |  2 +-
         .../power/{ => symmetric}/PEqualLimitation.java      |  2 +-
         .../{ => symmetric}/PGreaterEqualLimitation.java     |  2 +-
         .../{ => symmetric}/PSmallerEqualLimitation.java     |  2 +-
         .../power/{ => symmetric}/PowerChangeListener.java   |  2 +-
         .../power/{ => symmetric}/PowerException.java        |  2 +-
         .../power/{ => symmetric}/PowerResetListener.java    |  2 +-
         .../power/{ => symmetric}/QEqualLimitation.java      |  2 +-
         .../{ => symmetric}/QGreaterEqualLimitation.java     |  2 +-
         .../{ => symmetric}/QSmallerEqualLimitation.java     |  2 +-
         .../power/{ => symmetric}/SMaxLimitation.java        |  2 +-
         .../utilities/power/{ => symmetric}/SVGWriter.java   |  2 +-
         .../power/{ => symmetric}/SymmetricPower.java        |  4 ++--
         .../{ => symmetric}/SymmetricPowerClusterImpl.java   |  2 +-
         .../power/{ => symmetric}/SymmetricPowerImpl.java    |  2 +-
         .../core/utilities/power/{ => symmetric}/Test.java   |  2 +-
         .../controller/symmetric/avoidtotalcharge/Ess.java   |  6 +++---
         .../AvoidTotalDischargeController.java               |  2 +-
         .../symmetric/avoidtotaldischarge/Ess.java           |  6 +++---
         .../AvoidTotalDischargeSocTimeLineController.java    |  2 +-
         .../avoidtotaldischargesoctimeline/Ess.java          |  6 +++---
         .../symmetric/balancing/BalancingController.java     |  2 +-
         .../impl/controller/symmetric/balancing/Ess.java     |  4 ++--
         .../BalancingBandgapActivePowerController.java       |  2 +-
         .../BalancingBandgapReactivePowerController.java     |  2 +-
         .../controller/symmetric/balancingbandgap/Ess.java   |  6 +++---
         .../balancingcosphi/BalancingCosPhiController.java   |  2 +-
         .../controller/symmetric/balancingcosphi/Ess.java    |  4 ++--
         .../balancingcurrent/BalancingCurrentController.java |  2 +-
         .../controller/symmetric/balancingcurrent/Ess.java   |  4 ++--
         .../BalancingOffsetActivePowerController.java        |  2 +-
         .../BalancingOffsetReactivePowerController.java      |  2 +-
         .../controller/symmetric/balancingoffset/Ess.java    |  6 +++---
         .../balancingsurplus/BalancingSurplusController.java |  2 +-
         .../controller/symmetric/balancingsurplus/Ess.java   |  4 ++--
         .../capacitytest/CapacityTestController.java         |  2 +-
         .../impl/controller/symmetric/capacitytest/Ess.java  |  4 ++--
         .../symmetric/cosphi/CosPhiController.java           |  2 +-
         .../impl/controller/symmetric/cosphi/Ess.java        |  4 ++--
         .../CosPhiCharacteristicController.java              |  2 +-
         .../symmetric/cosphicharacteristic/Ess.java          |  4 ++--
         .../impl/controller/symmetric/fixvalue/Ess.java      |  6 +++---
         .../fixvalue/FixValueActivePowerController.java      |  2 +-
         .../fixvalue/FixValueReactivePowerController.java    |  2 +-
         .../symmetric/gridfeedinlimitation/Ess.java          |  4 ++--
         .../GridFeedInLimitationController.java              |  2 +-
         .../controller/symmetric/powerbyfrequency/Ess.java   |  4 ++--
         .../powerbyfrequency/PowerByFrequencyController.java |  2 +-
         .../ActivePowerLimitationController.java             |  2 +-
         .../controller/symmetric/powerlimitation/Ess.java    | 10 +++++-----
         .../ReactivePowerLimitationController.java           |  2 +-
         .../impl/controller/symmetric/powerramp/Ess.java     |  4 ++--
         .../symmetric/powerramp/PowerRampController.java     |  2 +-
         .../controller/symmetric/timelinecharge/Ess.java     |  6 +++---
         .../timelinecharge/TimelineChargeController.java     |  2 +-
         .../ActivePowerVoltageCharacteristicController.java  |  2 +-
         .../symmetric/voltagecharacteristic/Ess.java         |  6 +++---
         ...ReactivePowerVoltageCharacteristicController.java |  2 +-
         .../io/openems/impl/device/byd/Bem125ktla01Ess.java  |  2 +-
         .../impl/device/commercial/FeneconCommercialEss.java | 12 ++++++------
         .../io/openems/impl/device/mini/FeneconMiniEss.java  |  8 ++++----
         edge/src/io/openems/impl/device/refu/RefuEss.java    |  8 ++++----
         .../impl/device/simulator/SimulatorSymmetricEss.java |  6 +++---
         .../io/openems/impl/device/sma/SunnyIsland6Ess.java  |  6 +++---
         .../AsymmetricSymmetricCombinationEssNature.java     |  2 +-
         .../device/system/esscluster/EssClusterNature.java   |  4 ++--
         .../devicenatures/UnitTestSymmetricEssNature.java    |  2 +-
         74 files changed, 121 insertions(+), 121 deletions(-)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/CosPhiLineCharacteristicLimitation.java (98%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/CosPhiLineLimitation.java (97%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/Limitation.java (94%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/LimitationChangedListener.java (64%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/MaxCosPhiLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/NoPBetweenLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/NoQBetweenLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/PEqualLimitation.java (98%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/PGreaterEqualLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/PSmallerEqualLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/PowerChangeListener.java (71%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/PowerException.java (88%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/PowerResetListener.java (73%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/QEqualLimitation.java (97%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/QGreaterEqualLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/QSmallerEqualLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/SMaxLimitation.java (96%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/SVGWriter.java (99%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/SymmetricPower.java (98%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/SymmetricPowerClusterImpl.java (99%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/SymmetricPowerImpl.java (98%)
         rename edge/src/io/openems/core/utilities/power/{ => symmetric}/Test.java (98%)
        
        diff --git a/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java b/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java
        index dbdcd227c63..ffb67742df8 100644
        --- a/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java
        +++ b/edge/src/io/openems/api/device/nature/ess/SymmetricEssNature.java
        @@ -22,7 +22,7 @@
         
         import io.openems.api.channel.ReadChannel;
         import io.openems.api.doc.ChannelInfo;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         public interface SymmetricEssNature extends EssNature {
         	/*
        diff --git a/edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/CosPhiLineCharacteristicLimitation.java
        similarity index 98%
        rename from edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/CosPhiLineCharacteristicLimitation.java
        index d5986320878..2385378e756 100644
        --- a/edge/src/io/openems/core/utilities/power/CosPhiLineCharacteristicLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/CosPhiLineCharacteristicLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.util.ArrayList;
         import java.util.List;
        diff --git a/edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/CosPhiLineLimitation.java
        similarity index 97%
        rename from edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/CosPhiLineLimitation.java
        index a3fd7a92b96..f959bfce62b 100644
        --- a/edge/src/io/openems/core/utilities/power/CosPhiLineLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/CosPhiLineLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/Limitation.java b/edge/src/io/openems/core/utilities/power/symmetric/Limitation.java
        similarity index 94%
        rename from edge/src/io/openems/core/utilities/power/Limitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/Limitation.java
        index fd922867f83..b396c86050c 100644
        --- a/edge/src/io/openems/core/utilities/power/Limitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/Limitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.util.ArrayList;
         import java.util.List;
        diff --git a/edge/src/io/openems/core/utilities/power/LimitationChangedListener.java b/edge/src/io/openems/core/utilities/power/symmetric/LimitationChangedListener.java
        similarity index 64%
        rename from edge/src/io/openems/core/utilities/power/LimitationChangedListener.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/LimitationChangedListener.java
        index 97d190dc7a2..341610258bd 100644
        --- a/edge/src/io/openems/core/utilities/power/LimitationChangedListener.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/LimitationChangedListener.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         public interface LimitationChangedListener {
         
        diff --git a/edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/MaxCosPhiLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/MaxCosPhiLimitation.java
        index 58901c48271..28cb50a00d0 100644
        --- a/edge/src/io/openems/core/utilities/power/MaxCosPhiLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/MaxCosPhiLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/NoPBetweenLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/NoPBetweenLimitation.java
        index b851b643643..2388721a7bd 100644
        --- a/edge/src/io/openems/core/utilities/power/NoPBetweenLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/NoPBetweenLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/NoQBetweenLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/NoQBetweenLimitation.java
        index e53fc0b7a24..cbe167bca20 100644
        --- a/edge/src/io/openems/core/utilities/power/NoQBetweenLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/NoQBetweenLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/PEqualLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/PEqualLimitation.java
        similarity index 98%
        rename from edge/src/io/openems/core/utilities/power/PEqualLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/PEqualLimitation.java
        index cd102a3b6af..4d389e9672a 100644
        --- a/edge/src/io/openems/core/utilities/power/PEqualLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/PEqualLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/PGreaterEqualLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/PGreaterEqualLimitation.java
        index cddb311cea9..f42681dfbc2 100644
        --- a/edge/src/io/openems/core/utilities/power/PGreaterEqualLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/PGreaterEqualLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/PSmallerEqualLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/PSmallerEqualLimitation.java
        index 13de5aada04..6f74f05a847 100644
        --- a/edge/src/io/openems/core/utilities/power/PSmallerEqualLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/PSmallerEqualLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/PowerChangeListener.java b/edge/src/io/openems/core/utilities/power/symmetric/PowerChangeListener.java
        similarity index 71%
        rename from edge/src/io/openems/core/utilities/power/PowerChangeListener.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/PowerChangeListener.java
        index 1b516652246..9b2cc360358 100644
        --- a/edge/src/io/openems/core/utilities/power/PowerChangeListener.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/PowerChangeListener.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Geometry;
         
        diff --git a/edge/src/io/openems/core/utilities/power/PowerException.java b/edge/src/io/openems/core/utilities/power/symmetric/PowerException.java
        similarity index 88%
        rename from edge/src/io/openems/core/utilities/power/PowerException.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/PowerException.java
        index 077edd8e141..1a2e1bec5ca 100644
        --- a/edge/src/io/openems/core/utilities/power/PowerException.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/PowerException.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         public class PowerException extends Exception {
         
        diff --git a/edge/src/io/openems/core/utilities/power/PowerResetListener.java b/edge/src/io/openems/core/utilities/power/symmetric/PowerResetListener.java
        similarity index 73%
        rename from edge/src/io/openems/core/utilities/power/PowerResetListener.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/PowerResetListener.java
        index 7c91b202942..5b2c655b51c 100644
        --- a/edge/src/io/openems/core/utilities/power/PowerResetListener.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/PowerResetListener.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Geometry;
         
        diff --git a/edge/src/io/openems/core/utilities/power/QEqualLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/QEqualLimitation.java
        similarity index 97%
        rename from edge/src/io/openems/core/utilities/power/QEqualLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/QEqualLimitation.java
        index 954dea67cbf..f1e03e0d35d 100644
        --- a/edge/src/io/openems/core/utilities/power/QEqualLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/QEqualLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/QGreaterEqualLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/QGreaterEqualLimitation.java
        index 937bd1128c0..fc185dca26e 100644
        --- a/edge/src/io/openems/core/utilities/power/QGreaterEqualLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/QGreaterEqualLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/QSmallerEqualLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/QSmallerEqualLimitation.java
        index a3e2dc29e19..58d9903e5b1 100644
        --- a/edge/src/io/openems/core/utilities/power/QSmallerEqualLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/QSmallerEqualLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/SMaxLimitation.java b/edge/src/io/openems/core/utilities/power/symmetric/SMaxLimitation.java
        similarity index 96%
        rename from edge/src/io/openems/core/utilities/power/SMaxLimitation.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/SMaxLimitation.java
        index e0c1a642d8d..76b09bda3d6 100644
        --- a/edge/src/io/openems/core/utilities/power/SMaxLimitation.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/SMaxLimitation.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import com.vividsolutions.jts.geom.Coordinate;
         import com.vividsolutions.jts.geom.Geometry;
        diff --git a/edge/src/io/openems/core/utilities/power/SVGWriter.java b/edge/src/io/openems/core/utilities/power/symmetric/SVGWriter.java
        similarity index 99%
        rename from edge/src/io/openems/core/utilities/power/SVGWriter.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/SVGWriter.java
        index 27b03c9525b..13a29e6bf9f 100644
        --- a/edge/src/io/openems/core/utilities/power/SVGWriter.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/SVGWriter.java
        @@ -9,7 +9,7 @@
          *
          * http://www.eclipse.org/org/documents/edl-v10.php.
          */
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.io.IOException;
         import java.io.StringWriter;
        diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPower.java b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPower.java
        similarity index 98%
        rename from edge/src/io/openems/core/utilities/power/SymmetricPower.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/SymmetricPower.java
        index de32ec731c4..4b02f1a75d1 100644
        --- a/edge/src/io/openems/core/utilities/power/SymmetricPower.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPower.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.awt.Color;
         import java.util.ArrayList;
        @@ -197,7 +197,7 @@ public void removeListener(PowerChangeListener listener) {
         	}
         
         	/**
        -	 * Allyies a limit to the current power representing polygon.
        +	 * Applies a limit to the current power representing polygon.
         	 * @param limit the Limitation implementation to apply
         	 * @throws PowerException this Exception is thrown if the result is empty
         	 */
        diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java
        similarity index 99%
        rename from edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java
        index d524e96a231..51fefcb9494 100644
        --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerClusterImpl.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.util.ArrayList;
         import java.util.Collections;
        diff --git a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java
        similarity index 98%
        rename from edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java
        index 37692ed73ef..0664806d470 100644
        --- a/edge/src/io/openems/core/utilities/power/SymmetricPowerImpl.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.util.ArrayList;
         import java.util.List;
        diff --git a/edge/src/io/openems/core/utilities/power/Test.java b/edge/src/io/openems/core/utilities/power/symmetric/Test.java
        similarity index 98%
        rename from edge/src/io/openems/core/utilities/power/Test.java
        rename to edge/src/io/openems/core/utilities/power/symmetric/Test.java
        index 27e7183a899..6f182bef9dc 100644
        --- a/edge/src/io/openems/core/utilities/power/Test.java
        +++ b/edge/src/io/openems/core/utilities/power/symmetric/Test.java
        @@ -1,4 +1,4 @@
        -package io.openems.core.utilities.power;
        +package io.openems.core.utilities.power.symmetric;
         
         import java.util.ArrayList;
         import java.util.List;
        diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java
        index 37353438b3b..27d451db6b9 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotalcharge/Ess.java
        @@ -13,9 +13,9 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.core.utilities.hysteresis.Hysteresis;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java
        index c559a429960..ab314ca0e67 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java
        @@ -34,7 +34,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         import io.openems.impl.controller.symmetric.avoidtotaldischarge.Ess.State;
         
         @ThingInfo(title = "Avoid total discharge of battery (Symmetric)", description = "Makes sure the battery is not going into critically low state of charge. For symmetric Ess.")
        diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java
        index d40cdab7399..99852dc7a7b 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/Ess.java
        @@ -30,9 +30,9 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.core.utilities.hysteresis.Hysteresis;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java
        index 6d6df0aed4c..c246b8aad88 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java
        @@ -40,7 +40,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         import io.openems.impl.controller.symmetric.avoidtotaldischargesoctimeline.Ess.State;
         
         @ThingInfo(title = "Avoid total discharge of battery (Symmetric)", description = "Makes sure the battery is not going into critically low state of charge. For symmetric Ess.")
        diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java
        index 2163dc6697d..1b585832ff9 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/Ess.java
        @@ -29,9 +29,9 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java b/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java
        index 7a925cad7c3..839573b8a80 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancing/BalancingController.java
        @@ -32,7 +32,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Self-consumption optimization (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. Ess-Cluster is supported.")
         public class BalancingController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java
        index 051d14cd32b..ac94237fab6 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancing/Ess.java
        @@ -25,8 +25,8 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java
        index 3917abaa2d6..06493904cc4 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapActivePowerController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.")
         public class BalancingBandgapActivePowerController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java
        index aa2e740e3df..eaac5ed5ea3 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/BalancingBandgapReactivePowerController.java
        @@ -28,7 +28,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Balancing bandgap (Symmetric)", description = "Tries to keep the grid meter within a bandgap. For symmetric Ess.")
         public class BalancingBandgapReactivePowerController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java
        index 78b87063237..7050084ca3c 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingbandgap/Ess.java
        @@ -25,9 +25,9 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.QEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java
        index 07a8bdf80bc..8032c250c40 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/BalancingCosPhiController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Balancing Cos-Phi (Symmetric)", description = "Tries to keep the grid meter at a given cos-phi. For symmetric Ess.")
         public class BalancingCosPhiController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java
        index cf23acdf295..bda21ccace1 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcosphi/Ess.java
        @@ -24,8 +24,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.CosPhiLineLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.CosPhiLineLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java
        index a437224eec2..a6e21a798f7 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/BalancingCurrentController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Balancing current (Symmetric)", description = "Tries to keep the grid meter at a given current. For symmetric Ess.")
         public class BalancingCurrentController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java
        index eedc9537cfe..6eaadc2ad59 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingcurrent/Ess.java
        @@ -24,8 +24,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java
        index 154fc351012..6e49f252918 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetActivePowerController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         /*
          * this Controller calculates the power consumption of the house and charges or discharges the storages to reach zero power consumption from the grid
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java
        index f225d4169c5..e5c19191521 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/BalancingOffsetReactivePowerController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         /*
          * this Controller calculates the power consumption of the house and charges or discharges the storages to reach zero power consumption from the grid
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java
        index 8b39bb0ccd6..5927c15c530 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingoffset/Ess.java
        @@ -25,9 +25,9 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.QEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        index 5b5a702d099..81004e61342 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/BalancingSurplusController.java
        @@ -30,7 +30,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Self-consumption optimization with surplus feed-in (Symmetric)", description = "Tries to keep the grid meter on zero. For symmetric Ess. If ess is over the surplusMinSoc, the ess discharges with the power of the chargers. ")
         public class BalancingSurplusController extends Controller{
        diff --git a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java
        index c19721b2f65..c25d0d00c3d 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/balancingsurplus/Ess.java
        @@ -25,8 +25,8 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java b/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java
        index b440c8cf8d3..a0e9dcf6a4e 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/capacitytest/CapacityTestController.java
        @@ -37,7 +37,7 @@
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
         import io.openems.api.exception.WriteChannelException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Battery capacity test (Symmetric)", description = "Executes a capacity test. For symmetric Ess.")
         public class CapacityTestController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java b/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java
        index 78cd855acb3..d0e20484beb 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/capacitytest/Ess.java
        @@ -25,8 +25,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java b/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java
        index 725c02994d9..c5b15eef859 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/cosphi/CosPhiController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         import io.openems.impl.controller.symmetric.balancingcosphi.Ess;
         
         @ThingInfo(title = "Ess Cos-Phi (Symmetric)", description = "Keeps the Ess at a given cos-phi. For symmetric Ess.")
        diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java b/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java
        index ac5268e4928..8c059f21953 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/cosphi/Ess.java
        @@ -24,8 +24,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.CosPhiLineLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.CosPhiLineLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java
        index 3aacf0bc077..7923c79a61d 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/CosPhiCharacteristicController.java
        @@ -32,7 +32,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Cos-Phi Characteristics (Symmetric)")
         public class CosPhiCharacteristicController extends Controller implements ChannelChangeListener{
        diff --git a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java
        index dd7a1bdcfff..eb7e65bd747 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/cosphicharacteristic/Ess.java
        @@ -24,8 +24,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.CosPhiLineCharacteristicLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.CosPhiLineCharacteristicLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java
        index c56ead30190..1bee9bb0d40 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/Ess.java
        @@ -23,9 +23,9 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.QEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java
        index 68891f04af8..87df35d3ee7 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueActivePowerController.java
        @@ -28,7 +28,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Fixed active and reactive power (Symmetric)", description = "Charges or discharges the battery with a predefined, fixed power. For symmetric Ess.")
         public class FixValueActivePowerController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java
        index 771ec7a7819..619eecac1c9 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/fixvalue/FixValueReactivePowerController.java
        @@ -28,7 +28,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Fixed active and reactive power (Symmetric)", description = "Charges or discharges the battery with a predefined, fixed power. For symmetric Ess.")
         public class FixValueReactivePowerController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java
        index 9ec34d5d98f..7fdba9d3309 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/Ess.java
        @@ -24,8 +24,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java
        index 7a751a428c0..d33c550bf28 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/gridfeedinlimitation/GridFeedInLimitationController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "", description = "Calulates maximal Ess power to avoid grid feed in.")
         public class GridFeedInLimitationController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java
        index cb77e76d538..efe243d2633 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/Ess.java
        @@ -25,8 +25,8 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java
        index 7333ff0ef61..eaec64c5e68 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerbyfrequency/PowerByFrequencyController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Power by frequency (Symmetric)", description = "Tries to keep the grid meter at a given frequency. For symmetric Ess.")
         public class PowerByFrequencyController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java
        index 7836edcb997..15782db430c 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ActivePowerLimitationController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Power limitation (Symmetric)", description = "Limits the active and reactive power of the Ess. For symmetric Ess.")
         public class ActivePowerLimitationController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java
        index 2bb837439f3..331c78a7adf 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/Ess.java
        @@ -23,11 +23,11 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.QGreaterEqualLimitation;
        -import io.openems.core.utilities.power.QSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java
        index 90257ebc645..6679fdca673 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerlimitation/ReactivePowerLimitationController.java
        @@ -26,7 +26,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Power limitation (Symmetric)", description = "Limits the active and reactive power of the Ess. For symmetric Ess.")
         public class ReactivePowerLimitationController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java b/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java
        index c3a442469bb..bcccbaf55ad 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerramp/Ess.java
        @@ -24,8 +24,8 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java b/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java
        index 97d36ffb085..b5f075b72e3 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/powerramp/PowerRampController.java
        @@ -29,7 +29,7 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Power ramp (Symmetric)", description = "Follows a power ramp. For symmetric Ess.")
         public class PowerRampController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java
        index 05750f1cdaa..c7901118090 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/Ess.java
        @@ -24,9 +24,9 @@
         import io.openems.api.controller.IsThingMap;
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SMaxLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SMaxLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java
        index 21daf9c8d33..3be3a46999f 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java
        @@ -46,7 +46,7 @@
         import io.openems.common.session.Role;
         import io.openems.core.utilities.AvgFiFoQueue;
         import io.openems.core.utilities.JsonUtils;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Timeline charge (Symmetric)")
         public class TimelineChargeController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java
        index f3761be18d9..466b888c2b1 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ActivePowerVoltageCharacteristicController.java
        @@ -34,7 +34,7 @@
         import io.openems.api.exception.InvalidValueException;
         import io.openems.core.utilities.ControllerUtils;
         import io.openems.core.utilities.Point;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Voltage characteristics (Symmetric)")
         public class ActivePowerVoltageCharacteristicController extends Controller {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java
        index a93f07f2142..5a891425619 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/Ess.java
        @@ -25,9 +25,9 @@
         import io.openems.api.controller.ThingMap;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.exception.InvalidValueException;
        -import io.openems.core.utilities.power.PEqualLimitation;
        -import io.openems.core.utilities.power.QEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.PEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         
         @IsThingMap(type = SymmetricEssNature.class)
         public class Ess extends ThingMap {
        diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java
        index 7bcca9ef4d3..aee7a876646 100644
        --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java
        +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/ReactivePowerVoltageCharacteristicController.java
        @@ -34,7 +34,7 @@
         import io.openems.api.exception.InvalidValueException;
         import io.openems.core.utilities.ControllerUtils;
         import io.openems.core.utilities.Point;
        -import io.openems.core.utilities.power.PowerException;
        +import io.openems.core.utilities.power.symmetric.PowerException;
         
         @ThingInfo(title = "Voltage characteristics (Symmetric)")
         public class ReactivePowerVoltageCharacteristicController extends Controller {
        diff --git a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java
        index c1c2bf3d38a..6cffe979dc4 100644
        --- a/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java
        +++ b/edge/src/io/openems/impl/device/byd/Bem125ktla01Ess.java
        @@ -29,7 +29,7 @@
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel;
         import io.openems.impl.protocol.modbus.ModbusDeviceNature;
         import io.openems.impl.protocol.modbus.ModbusReadChannel;
        diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java
        index 1bb22266ed0..4780c14a085 100644
        --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java
        +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java
        @@ -33,12 +33,12 @@
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.QGreaterEqualLimitation;
        -import io.openems.core.utilities.power.QSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SMaxLimitation;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.QSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SMaxLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel;
         import io.openems.impl.protocol.modbus.ModbusDeviceNature;
         import io.openems.impl.protocol.modbus.ModbusReadLongChannel;
        diff --git a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java
        index 9d0e99cb5dd..625ec880e03 100644
        --- a/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java
        +++ b/edge/src/io/openems/impl/device/mini/FeneconMiniEss.java
        @@ -39,10 +39,10 @@
         import io.openems.api.exception.ConfigException;
         import io.openems.api.exception.InvalidValueException;
         import io.openems.core.utilities.ControllerUtils;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SMaxLimitation;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SMaxLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel;
         import io.openems.impl.protocol.modbus.ModbusDeviceNature;
         import io.openems.impl.protocol.modbus.ModbusReadLongChannel;
        diff --git a/edge/src/io/openems/impl/device/refu/RefuEss.java b/edge/src/io/openems/impl/device/refu/RefuEss.java
        index 93d0efd0b0c..931f0bbfa40 100644
        --- a/edge/src/io/openems/impl/device/refu/RefuEss.java
        +++ b/edge/src/io/openems/impl/device/refu/RefuEss.java
        @@ -35,10 +35,10 @@
         import io.openems.api.device.nature.ess.SymmetricEssNature;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
        -import io.openems.core.utilities.power.NoPBetweenLimitation;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.NoPBetweenLimitation;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.protocol.modbus.ModbusBitWrappingChannel;
         import io.openems.impl.protocol.modbus.ModbusDeviceNature;
         import io.openems.impl.protocol.modbus.ModbusReadLongChannel;
        diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java
        index 3bee1b6309d..a06523e7b87 100644
        --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java
        +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java
        @@ -51,9 +51,9 @@
         import io.openems.core.ThingRepository;
         import io.openems.core.utilities.AvgFiFoQueue;
         import io.openems.core.utilities.ControllerUtils;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.protocol.modbus.FaultModbus;
         import io.openems.impl.protocol.modbus.ModbusWriteLongChannel;
         import io.openems.impl.protocol.simulator.SimulatorDeviceNature;
        diff --git a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java
        index 4f91a89f1f8..cc4ffabbb29 100644
        --- a/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java
        +++ b/edge/src/io/openems/impl/device/sma/SunnyIsland6Ess.java
        @@ -14,9 +14,9 @@
         import io.openems.api.doc.ChannelInfo;
         import io.openems.api.doc.ThingInfo;
         import io.openems.api.exception.ConfigException;
        -import io.openems.core.utilities.power.PGreaterEqualLimitation;
        -import io.openems.core.utilities.power.PSmallerEqualLimitation;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.PGreaterEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.PSmallerEqualLimitation;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.protocol.modbus.ModbusDeviceNature;
         import io.openems.impl.protocol.modbus.ModbusReadLongChannel;
         import io.openems.impl.protocol.modbus.ModbusWriteLongChannel;
        diff --git a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java
        index d95da3d5a2f..e77b3d46363 100644
        --- a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java
        +++ b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java
        @@ -32,7 +32,7 @@
         import io.openems.core.Config;
         import io.openems.core.ThingRepository;
         import io.openems.core.utilities.ControllerUtils;
        -import io.openems.core.utilities.power.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
         import io.openems.impl.protocol.system.SystemDeviceNature;
         
         @ThingInfo(title = "Ess Asymmetric-Symmetric-Combination")
        diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java
        index d50abf285d9..6f5ac10e4f1 100644
        --- a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java
        +++ b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java
        @@ -32,8 +32,8 @@
         import io.openems.core.BridgeInitializedEventListener;
         import io.openems.core.Config;
         import io.openems.core.ThingRepository;
        -import io.openems.core.utilities.power.SymmetricPower;
        -import io.openems.core.utilities.power.SymmetricPowerClusterImpl;
        +import io.openems.core.utilities.power.symmetric.SymmetricPower;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerClusterImpl;
         import io.openems.impl.protocol.system.SystemDeviceNature;
         
         @ThingInfo(title = "Ess Cluster")
        diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java
        index ec11efb0d47..2888a3b2f7e 100644
        --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java
        +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java
        @@ -13,7 +13,7 @@
         import io.openems.api.device.Device;
         import io.openems.api.device.nature.ess.EssNature;
         import io.openems.api.device.nature.ess.SymmetricEssNature;
        -import io.openems.core.utilities.power.SymmetricPowerImpl;
        +import io.openems.core.utilities.power.symmetric.SymmetricPowerImpl;
         import io.openems.impl.device.simulator.SimulatorTools;
         import io.openems.test.utils.channel.UnitTestConfigChannel;
         import io.openems.test.utils.channel.UnitTestReadChannel;
        
        From 08879eae0b0b13d3354a810e3065d9614162e4dc Mon Sep 17 00:00:00 2001
        From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= 
        Date: Thu, 1 Mar 2018 14:58:12 +0100
        Subject: [PATCH 110/156] clean up code and add unhandled cases
        
        ---
         .../ChannelThresholdScheduler.java              | 17 ++++++-----------
         1 file changed, 6 insertions(+), 11 deletions(-)
        
        diff --git a/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java b/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java
        index 48d0853a35c..903f5216d9f 100644
        --- a/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java
        +++ b/edge/src/io/openems/impl/scheduler/channelthreshold/ChannelThresholdScheduler.java
        @@ -237,24 +237,19 @@ private void loadThresholds() throws InvalidValueException {
         			}
         		}
         		Collections.sort(thresholdCollection, (c1, c2) -> c1.threshold.compareTo(c2.threshold));
        -		ControllerHysteresis lastHysteresis = null;
        +		ControllerHysteresis lastHysteresis = new ControllerHysteresis();
        +		lastHysteresis.min = Long.MIN_VALUE;
         		for (Threshold t : thresholdCollection) {
         			ControllerHysteresis ch = new ControllerHysteresis();
         			ch.min = t.threshold;
        -			if (lastHysteresis != null) {
        -				lastHysteresis.max = t.threshold + t.hysteresis;
        -			}
         			ch.below = lastHysteresis;
         			ch.controllers.addAll(t.controllers);
        -			if (lastHysteresis != null) {
        -				lastHysteresis.above = ch;
        -			}
        +			lastHysteresis.max = t.threshold + t.hysteresis;
        +			lastHysteresis.above = ch;
         			lastHysteresis = ch;
         		}
        -		if (lastHysteresis != null) {
        -			lastHysteresis.max = Long.MAX_VALUE;
        -		}
        -		if (thresholdChannel.valueOptional().isPresent() && lastHysteresis != null) {
        +		lastHysteresis.max = Long.MAX_VALUE;
        +		if (thresholdChannel.valueOptional().isPresent() && lastHysteresis != null && lastHysteresis.max > thresholdChannel.value()) {
         			while (lastHysteresis.below != null) {
         				if (lastHysteresis.isBetween(thresholdChannel.value())) {
         					break;
        
        From 6cca42dac28a12eafa57c9e961a9cc62e79c336a Mon Sep 17 00:00:00 2001
        From: Stefan Feilmeier 
        Date: Thu, 1 Mar 2018 22:53:25 +0100
        Subject: [PATCH 111/156] Start with new websocket protocol Edge <-> UI
        
        ---
         doc/readme.md                                 |   2 +-
         edge/pom.xml                                  |  32 +-
         .../websocket/EdgeWebsocketHandler.java       |   8 +
         .../websocket/WebsocketLogAppender.java       |   2 +-
         .../api/websocket/UiEdgeWebsocketHandler.java |  23 ++
         .../api/websocket/WebsocketApiController.java |   4 +-
         .../api/websocket/WebsocketApiServer.java     | 309 +++++++++++-------
         .../impl/provider/UiWebsocketServer.java      |   6 +-
         .../websocket/AbstractWebsocketServer.java    | 120 ++-----
         .../common/websocket/DefaultMessages.java     |  10 +-
         ui/src/app/shared/service/websocket.ts        |  18 -
         ui/src/environments/environment.ts            |   6 +-
         12 files changed, 294 insertions(+), 246 deletions(-)
         create mode 100644 edge/src/io/openems/impl/controller/api/websocket/UiEdgeWebsocketHandler.java
        
        diff --git a/doc/readme.md b/doc/readme.md
        index ea4213fb90a..fe6072d705a 100644
        --- a/doc/readme.md
        +++ b/doc/readme.md
        @@ -60,7 +60,7 @@ currently forwarded to Odoo login page
         			id: number,
         			name: string,
         			comment: string,
        -			producttype: "Pro 9-12" | "MiniES 3-3" | "PRO Hybrid 9-10" | "PRO Compact 3-10" | "COMMERCIAL 40-45" | "INDUSTRIAL",
        +			producttype: "Pro 9-12" | "MiniES 3-3" | "PRO Hybrid 9-10" | "PRO Compact 3-10" | "COMMERCIAL 40-45" | "INDUSTRIAL" | "",
         			role: "admin" | "installer" | "owner" | "guest",
         			online: boolean
         		}]
        diff --git a/edge/pom.xml b/edge/pom.xml
        index 3c4f8cb119a..0ccc86ff8fd 100644
        --- a/edge/pom.xml
        +++ b/edge/pom.xml
        @@ -26,7 +26,7 @@
         		2.3.10
         		1.1
         		1.7.25
        -		1.3.6
        +		1.3.7
         	
         	
         		
        @@ -46,11 +46,6 @@
         			guava
         			${guava.version}
         		
        -		
        -			org.influxdb
        -			influxdb-java
        -			${influxdb.version}
        -		
         		
         			com.ghgande
         			j2mod
        @@ -91,6 +86,31 @@
         			commons-cli
         			${commons-cli.version}
         		
        +		
        +			org.apache.servicemix.bundles
        +			org.apache.servicemix.bundles.influxdb-java
        +			2.3_1
        +		
        +		
        +			org.apache.servicemix.bundles
        +			org.apache.servicemix.bundles.retrofit
        +			1.9.0_1
        +		
        +		
        +			org.apache.servicemix.bundles
        +			org.apache.servicemix.bundles.influxdb-java
        +			2.3_1
        +		
        +		
        +			org.apache.servicemix.bundles
        +			org.apache.servicemix.bundles.okhttp
        +			2.7.5_1
        +		
        +		
        +			org.apache.servicemix.bundles
        +			org.apache.servicemix.bundles.okio
        +			1.13.0_1
        +		
         	
         	
         		
        diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        index 32766658dae..4afe4a2473b 100644
        --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java
        @@ -103,6 +103,14 @@ public EdgeWebsocketHandler(ApiWorker apiWorker, WebSocket websocket, Role role)
         		this.roleOpt = Optional.ofNullable(role);
         	}
         
        +	public void dispose() {
        +		// TODO dispose everything
        +		if(this.websocketOpt.isPresent()) {
        +			log.info("Dispose: Closing websocket");
        +			this.websocketOpt.get().close();
        +		}
        +	}
        +
         	public void setWebsocket(WebSocket websocket) {
         		this.websocketOpt = Optional.ofNullable(websocket);
         	}
        diff --git a/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java b/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java
        index 73692dcc495..fd5d4de3f80 100644
        --- a/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java
        +++ b/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java
        @@ -22,7 +22,7 @@ protected void append(ILoggingEvent event) {
         			for (Controller controller : scheduler.getControllers()) {
         				if (controller instanceof WebsocketApiController) {
         					WebsocketApiController websocketApiController = (WebsocketApiController) controller;
        -					websocketApiController.broadcastLog(timestamp, level, source, message);
        +					websocketApiController.sendLog(timestamp, level, source, message);
         				}
         			}
         		}
        diff --git a/edge/src/io/openems/impl/controller/api/websocket/UiEdgeWebsocketHandler.java b/edge/src/io/openems/impl/controller/api/websocket/UiEdgeWebsocketHandler.java
        new file mode 100644
        index 00000000000..60d77734c67
        --- /dev/null
        +++ b/edge/src/io/openems/impl/controller/api/websocket/UiEdgeWebsocketHandler.java
        @@ -0,0 +1,23 @@
        +package io.openems.impl.controller.api.websocket;
        +
        +import io.openems.api.security.User;
        +import io.openems.core.utilities.websocket.EdgeWebsocketHandler;
        +
        +public class UiEdgeWebsocketHandler extends EdgeWebsocketHandler {
        +
        +	private final String token;
        +	private final User user;
        +
        +	public UiEdgeWebsocketHandler(String token, User user) {
        +		this.token = token;
        +		this.user = user;
        +	}
        +
        +	public String getToken() {
        +		return token;
        +	}
        +
        +	public User getUser() {
        +		return user;
        +	}
        +}
        diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java
        index 8cdce4dc043..c9b67089286 100644
        --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java
        +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java
        @@ -112,8 +112,8 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol
         	 *
         	 * @param jMessage
         	 */
        -	public void broadcastLog(long timestamp, String level, String source, String message) {
        -		this.websocketApiServer.broadcastLog(timestamp, level, source, message);
        +	public void sendLog(long timestamp, String level, String source, String message) {
        +		this.websocketApiServer.sendLog(timestamp, level, source, message);
         	}
         
         	@Override
        diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java
        index 0be2399aac1..88aa9de02bf 100644
        --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java
        +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java
        @@ -20,24 +20,38 @@
          *******************************************************************************/
         package io.openems.impl.controller.api.websocket;
         
        +import java.math.BigInteger;
        +import java.security.SecureRandom;
        +import java.util.HashMap;
        +import java.util.Map;
         import java.util.Optional;
         
         import org.java_websocket.WebSocket;
        +import org.java_websocket.framing.CloseFrame;
         import org.java_websocket.handshake.ClientHandshake;
         import org.slf4j.Logger;
         import org.slf4j.LoggerFactory;
         
        -import com.google.gson.JsonElement;
        +import com.google.gson.JsonArray;
         import com.google.gson.JsonObject;
         
        +import io.openems.api.security.User;
        +import io.openems.common.exceptions.OpenemsException;
        +import io.openems.common.utils.JsonUtils;
        +import io.openems.common.utils.SecureRandomSingleton;
         import io.openems.common.websocket.AbstractWebsocketServer;
        +import io.openems.common.websocket.DefaultMessages;
        +import io.openems.common.websocket.LogBehaviour;
        +import io.openems.common.websocket.Notification;
        +import io.openems.common.websocket.WebSocketUtils;
         import io.openems.core.utilities.api.ApiWorker;
        -import io.openems.impl.controller.api.websocket.session.WebsocketApiSession;
         
         public class WebsocketApiServer extends AbstractWebsocketServer {
         
         	private static Logger log = LoggerFactory.getLogger(WebsocketApiServer.class);
        +	private final Map handlers = new HashMap<>();
         	private final ApiWorker apiWorker;
        +	private final static int TOKEN_LENGTH = 130;
         
         	public WebsocketApiServer(ApiWorker apiWorker, int port) {
         		super(port);
        @@ -45,144 +59,207 @@ public WebsocketApiServer(ApiWorker apiWorker, int port) {
         	}
         
         	/**
        -	 * Open event of websocket. Parses the Odoo "session_id" and stores it in a new Session.
        +	 * Open event of websocket.
         	 */
         	@Override
         	protected void _onOpen(WebSocket websocket, ClientHandshake handshake) {
        -		//		JsonObject jHandshake = this.parseCookieFromHandshake(handshake);
        -		//		Optional tokenOpt = JsonUtils.getAsOptionalString(jHandshake, "token");
        -		//		if (tokenOpt.isPresent()) {
        -		//			String token = tokenOpt.get();
        -		//			Optional sessionOpt = this.sessionManager.getSessionByToken(token);
        -		//			if (sessionOpt.isPresent()) {
        -		//				WebsocketApiSession session = sessionOpt.get();
        -		//				Optional oldWebsocketOpt = session.getData().getWebsocketHandler().getWebsocket();
        -		//				if (oldWebsocketOpt.isPresent()) {
        -		//					// TODO to avoid this, websockets needs to be able to handle more than one websocket per session
        -		//					oldWebsocketOpt.get().closeConnection(CloseFrame.REFUSE,
        -		//							"Another client connected with this token");
        -		//				}
        -		//				// refresh session
        -		//				session.getData().getWebsocketHandler().setWebsocket(websocket);
        -		//				// add to websockets
        -		//				this.addWebsocket(websocket, session);
        -		//				// send connection successful to browser
        -		//				JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(),
        -		//						Optional.of(session.getData().getRole()), new JsonArray());
        -		//				log.info("Websocket connected by session. User [" + session.getData().getUser() + "] Session ["
        -		//						+ session.getToken() + "]");
        -		//				WebSocketUtils.send(websocket, jReply);
        -		//				return;
        -		//			}
        -		//			// if we are here, automatic authentication was not possible -> notify client
        -		//			WebSocketUtils.sendNotification(websocket, new JsonArray(), LogBehaviour.WRITE_TO_LOG,
        -		//					Notification.EDGE_AUTHENTICATION_BY_TOKEN_FAILED, tokenOpt.orElse(""));
        -		//		}
        +		// login using token from the cookie
        +		Optional tokenOpt =  getFieldFromHandshakeCookie(handshake, "token");
        +		if (tokenOpt.isPresent()) {
        +			String token = tokenOpt.get();
        +			UiEdgeWebsocketHandler handler = this.handlers.get(token);
        +			if (handler != null) {
        +				try {
        +					// Authentication successful
        +					this.handleAuthenticationSuccessful(handler.getUser(), websocket);
        +					log.info("User [" + getUserName(websocket) + "] logged in by token");
        +					return;
        +				} catch (OpenemsException e) {
        +					// TODO handle error
        +					log.error(e.getMessage());
        +				}
        +			}
        +		}
        +
        +		// if we are here, automatic authentication was not possible -> notify client
        +		WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */,
        +				LogBehaviour.WRITE_TO_LOG, Notification.EDGE_AUTHENTICATION_BY_TOKEN_FAILED, tokenOpt.orElse(""));
         	}
         
         	@Override
         	protected void _onMessage(WebSocket websocket, JsonObject jMessage) {
        -		//		// log.info("RECV: websocket[" + websocket + "]" + jMessage.toString());
        -		//
        -		//		/*
        -		//		 * Authenticate
        -		//		 */
        -		//		Optional sessionOpt = Optional.empty();
        -		//		if (jMessage.has("authenticate")) {
        -		//			// authenticate by username/password
        -		//			sessionOpt = authenticate(jMessage.get("authenticate"), websocket);
        -		//		}
        -		//		if (!sessionOpt.isPresent()) {
        -		//			// check if there is an existing session
        -		//			sessionOpt = this.getSessionFromWebsocket(websocket);
        -		//		}
        -		//		if (!sessionOpt.isPresent()) {
        -		//			/*
        -		//			 * send authentication failed reply
        -		//			 */
        -		//			JsonObject jReply = DefaultMessages.browserConnectionFailedReply();
        -		//			WebSocketUtils.send(websocket, jReply);
        -		//			websocket.closeConnection(CloseFrame.REFUSE, "Authentication failed");
        -		//			return;
        -		//		}
        -		//		WebsocketApiSession session = sessionOpt.get();
        -		//		/*
        -		//		 * On initial authentication...
        -		//		 */
        -		//		if (jMessage.has("authenticate")) {
        -		//			// add to websockets
        -		//			this.addWebsocket(websocket, session);
        -		//			// send connection successful to browser
        -		//			JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply(session.getToken(),
        -		//					Optional.of(session.getData().getRole()), new ArrayList<>());
        -		//			log.info("Browser connected by authentication. User [" + session.getData().getUser() + "] Session ["
        -		//					+ session.getToken() + "]");
        -		//			WebSocketUtils.send(websocket, jReply);
        -		//		}
        -		//
        -		//		/*
        -		//		 * Rest -> forward to websocket handler
        -		//		 */
        -		//		session.getData().getWebsocketHandler().onMessage(jMessage);
        +		/*
        +		 * Authenticate
        +		 */
        +		Optional jAuthenticateOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "authenticate");
        +		if (jAuthenticateOpt.isPresent()) {
        +			// authenticate by username/password
        +			try {
        +				authenticate(jAuthenticateOpt.get(), websocket);
        +			} catch (OpenemsException e) {
        +				// TODO error
        +				log.error(e.getMessage());
        +			}
        +			return;
        +		}
        +
        +		// get current Token
        +		String token = websocket.getAttachment();
        +		if (token == null) {
        +			// TODO error: no token
        +			return;
        +		}
        +		UiEdgeWebsocketHandler handler = this.handlers.get(token);
        +		if (handler == null) {
        +			// TODO error: no handler
        +			return;
        +		}
        +
        +		/*
        +		 * Rest -> forward to websocket handler
        +		 */
        +		handler.onMessage(jMessage);
         	}
         
         	@Override
         	protected void _onClose(WebSocket websocket) {
        -		// nothing to do here
        +		log.info("User [" + getUserName(websocket) + "] closed websocket connection");
         	}
         
         	/**
        -	 * Authenticates a user according to the "authenticate" message. Stores the session if valid.
        +	 * Authenticates a user according to the "authenticate" message. Stores and returnes the Role if valid.
         	 *
         	 * @param jAuthenticateElement
         	 * @param handler
        +	 * @throws OpenemsException
         	 */
        -	private Optional authenticate(JsonElement jAuthenticateElement, WebSocket websocket) {
        -		//		try {
        -		//			JsonObject jAuthenticate = JsonUtils.getAsJsonObject(jAuthenticateElement);
        -		//			if (jAuthenticate.has("mode")) {
        -		//				String mode = JsonUtils.getAsString(jAuthenticate, "mode");
        -		//				if (mode.equals("login")) {
        -		//					if (jAuthenticate.has("password")) {
        -		//						/*
        -		//						 * Authenticate using username and password
        -		//						 */
        -		//						String password = JsonUtils.getAsString(jAuthenticate, "password");
        -		//						if (jAuthenticate.has("username")) {
        -		//							String username = JsonUtils.getAsString(jAuthenticate, "username");
        -		//							return this.sessionManager.authByUserPassword(username, password, websocket, apiWorker);
        -		//						} else {
        -		//							return this.sessionManager.authByPassword(password, websocket, apiWorker);
        -		//						}
        -		//					}
        -		//
        -		//				} else if (mode.equals("logout")) {
        -		//					/*
        -		//					 * Logout and close session
        -		//					 */
        -		//					this.removeWebsocket(websocket);
        -		//				}
        -		//			}
        -		//		} catch (OpenemsException e) { /* ignore */ }
        -		return Optional.empty();
        +	private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws OpenemsException {
        +		if (jAuthenticate.has("mode")) {
        +			String mode = JsonUtils.getAsString(jAuthenticate, "mode");
        +			switch (mode) {
        +			case "login":
        +				try {
        +					/*
        +					 * Authenticate using password (and optionally username)
        +					 */
        +					String password = JsonUtils.getAsString(jAuthenticate, "password");
        +					Optional usernameOpt = JsonUtils.getAsOptionalString(jAuthenticate, "username");
        +					Optional userOpt;
        +					if (usernameOpt.isPresent()) {
        +						userOpt = User.authenticate(usernameOpt.get(), password);
        +					} else {
        +						userOpt = User.authenticate(password);
        +					}
        +
        +					if (!userOpt.isPresent()) {
        +						throw new OpenemsException("Authentication failed");
        +					}
        +					// authentication successful
        +					User user = userOpt.get();
        +					log.info("User [" + user.getName() + "] logged in by username/password");
        +					this.handleAuthenticationSuccessful(user, websocket);
        +
        +				} catch (OpenemsException e) {
        +					/*
        +					 * send authentication failed reply
        +					 */
        +					JsonObject jReply = DefaultMessages.uiConnectionFailedReply();
        +					WebSocketUtils.send(websocket, jReply);
        +					websocket.closeConnection(CloseFrame.REFUSE, "Error while authenticating: " + e.getMessage());
        +					return;
        +				}
        +				break;
        +			case "logout":
        +				/*
        +				 * Logout and close session
        +				 */
        +				String token = websocket.getAttachment();
        +				if (token != null) {
        +					UiEdgeWebsocketHandler handler = this.handlers.get(token);
        +					if (handler != null) {
        +						handler.dispose();
        +					}
        +					this.handlers.remove(token);
        +					log.info("User [" + getUserName(websocket) + "] logged out. Invalidated token [" + token + "]");
        +					// TODO send notification
        +				} else {
        +					log.warn("User tries to log out, but was not logged in.");
        +				}
        +				websocket.setAttachment(null);
        +			}
        +		}
        +
        +	}
        +
        +	private void handleAuthenticationSuccessful(User user, WebSocket websocket) throws OpenemsException {
        +		// Create Edges entry
        +		JsonObject jEdge = new JsonObject();
        +		jEdge.addProperty("id", 0);
        +		jEdge.addProperty("name", "fems0");
        +		jEdge.addProperty("comment", "FEMS");
        +		jEdge.addProperty("producttype", "");
        +		jEdge.addProperty("role", user.getRole().toString().toLowerCase());
        +		jEdge.addProperty("online", true);
        +		JsonArray jEdges = new JsonArray();
        +		jEdges.add(jEdge);
        +
        +		// invalidate old handler if exists
        +		String token = websocket.getAttachment();
        +		if (token != null) {
        +			UiEdgeWebsocketHandler oldHandler = this.handlers.get(token);
        +			oldHandler.dispose();
        +			this.handlers.remove(token);
        +		} else {
        +			// Generate token (source: http://stackoverflow.com/a/41156)
        +			SecureRandom sr = SecureRandomSingleton.getInstance();
        +			token = new BigInteger(TOKEN_LENGTH, sr).toString(32);
        +		}
        +
        +		// create new Handler
        +		UiEdgeWebsocketHandler handler = new UiEdgeWebsocketHandler(token, user);
        +
        +		// save
        +		this.handlers.put(token, handler);
        +		websocket.setAttachment(token);
        +
        +		// send reply
        +		JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply(token, jEdges);
        +		WebSocketUtils.send(websocket, jReply);
        +	}
        +
        +	@Override
        +	protected void _onError(WebSocket websocket, Exception ex) {
        +		log.warn("User [" + getUserName(websocket) + "] error: " + ex.getMessage());
         	}
         
         	/**
        -	 * Send a log entry to all connected websockets
        -	 *
        -	 * @param string
        -	 * @param timestamp
        +	 * Get token from handshake
         	 *
        -	 * @param jMessage
        +	 * @param handshake
        +	 * @return cookie as JsonObject
         	 */
        -	public void broadcastLog(long timestamp, String level, String source, String message) {
        -		//		for (WebsocketApiSession session : this.sessionManager.getSessions()) {
        -		//			session.getData().getWebsocketHandler().sendLog(timestamp, level, source, message);
        -		//		}
        +	private Optional getTokenFromHandshake(ClientHandshake handshake) {
        +		if (handshake.hasFieldValue("token")) {
        +			return Optional.ofNullable(handshake.getFieldValue("token"));
        +		}
        +		return Optional.empty();
         	}
         
        -	@Override
        -	protected void _onError(WebSocket websocket, Exception ex) {
        -		// TODO Auto-generated method stub
        +	private String getUserName(WebSocket websocket) {
        +		String token = websocket.getAttachment();
        +		if (token != null) {
        +			UiEdgeWebsocketHandler handler = this.handlers.get(token);
        +			if(handler != null) {
        +				User user = handler.getUser();
        +				return user.getName();
        +			}
        +		}
        +		return "UNKNOWN";
        +	}
        +
        +	public void sendLog(long timestamp, String level, String source, String message) {
        +		for(UiEdgeWebsocketHandler handler : this.handlers.values()) {
        +			handler.sendLog(timestamp, level, source, message);
        +		}
         	}
         }
        diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java
        index d441980c6ab..099dfa8ea5d 100644
        --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java
        +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java
        @@ -50,7 +50,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) {
         		User user;
         
         		// login using session_id from the cookie
        -		Optional sessionIdOpt = getSessionIdFromHandshake(handshake);
        +		Optional sessionIdOpt = getFieldFromHandshakeCookie(handshake, "session_id");
         		try {
         			if (!sessionIdOpt.isPresent()) {
         				throw new OpenemsException("Session-ID is missing in handshake");
        @@ -90,8 +90,8 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) {
         			}
         		}
         		log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "].");
        -		JsonObject jReply = DefaultMessages.browserConnectionSuccessfulReply("" /* TODO empty token? */,
        -				Optional.empty(), jEdges);
        +		JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply("" /* empty token? */,
        +				jEdges);
         		WebSocketUtils.sendOrLogError(websocket, jReply);
         	}
         
        diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java
        index 86deb78c5bc..27536a18329 100644
        --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java
        +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java
        @@ -72,22 +72,11 @@ public final void onMessage(WebSocket websocket, String message) {
         	 */
         	@Override
         	public final void onClose(WebSocket websocket, int code, String reason, boolean remote) {
        -		// try {
        -		// Optional sessionOpt = Optional.ofNullable(this.websockets.get(websocket));
        -		// String sessionString;
        -		// if (sessionOpt.isPresent()) {
        -		// sessionString = sessionOpt.get().toString() + " ";
        -		// } else {
        -		// sessionString = "";
        -		// }
        -		// log.info(sessionString + "Websocket closed. Code [" + code + "] Reason [" +
        -		// reason + "]");
        -		// this.websockets.remove(websocket);
        -		this._onClose(websocket);
        -		// } catch (Throwable e) {
        -		// log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " +
        -		// e.getMessage());
        -		// }
        +		try {
        +			this._onClose(websocket);
        +		} catch (Throwable e) {
        +			log.error("onClose-Error. Code [" + code + "] Reason [" + reason + "]: " + e.getMessage());
        +		}
         	}
         
         	/**
        @@ -95,35 +84,11 @@ public final void onClose(WebSocket websocket, int code, String reason, boolean
         	 */
         	@Override
         	public final void onError(WebSocket websocket, Exception ex) {
        -		// S session = this.websockets.get(websocket);
        -		// String sessionString;
        -		// if (session == null) {
        -		// sessionString = "";
        -		// } else {
        -		// sessionString = session.toString();
        -		// }
        -		// log.warn(sessionString + " Websocket error: " + ex.getMessage());
        -		this._onError(websocket, ex);
        -	}
        -
        -	/**
        -	 * Get cookie from handshake
        -	 *
        -	 * @param handshake
        -	 * @return cookie as JsonObject
        -	 */
        -	protected Optional getSessionIdFromHandshake(ClientHandshake handshake) {
        -		JsonObject jCookie = new JsonObject();
        -		if (handshake.hasFieldValue("cookie")) {
        -			String cookieString = handshake.getFieldValue("cookie");
        -			for (String cookieVariable : cookieString.split("; ")) {
        -				String[] keyValue = cookieVariable.split("=");
        -				if (keyValue.length == 2) {
        -					jCookie.addProperty(keyValue[0], keyValue[1]);
        -				}
        -			}
        +		try {
        +			this._onError(websocket, ex);
        +		} catch (Throwable e) {
        +			log.error("onError handling of Exception [" + ex.getMessage() + "] failed: " + e.getMessage());
         		}
        -		return JsonUtils.getAsOptionalString(jCookie, "session_id");
         	}
         
         	/**
        @@ -141,59 +106,14 @@ protected JsonObject handshakeToJsonObject(ClientHandshake handshake) {
         		return j;
         	}
         
        -	/**
        -	 * Returns the Websocket for the given token
        -	 *
        -	 * @param name
        -	 * @return
        -	 */
        -	// public Optional getWebsocketByToken(String token) {
        -	// Optional sessionOpt = (Optional)
        -	// this.sessionManager.getSessionByToken(token);
        -	// if (!sessionOpt.isPresent()) {
        -	// return Optional.empty();
        -	// }
        -	// S session = sessionOpt.get();
        -	// return Optional.ofNullable(this.websockets.inverse().get(session));
        -	// }
        -	//
        -	// protected void addWebsocket(WebSocket websocket, S session) {
        -	// synchronized (this.websockets) {
        -	// if (this.websockets.containsKey(websocket)) {
        -	// log.warn("Websocket [" + websocket + "] already existed. Replacing existing
        -	// one with session ["
        -	// + session + "]");
        -	// }
        -	// if (this.websockets.inverse().containsKey(session)) {
        -	// log.warn("Session [" + session + "] already existed. Replacing existing one
        -	// with websocket ["
        -	// + websocket + "]");
        -	// }
        -	// this.websockets.forcePut(websocket, session);
        -	// }
        -	// }
        -	//
        -	// protected void removeWebsocket(WebSocket websocket) {
        -	// synchronized (this.websockets) {
        -	// this.websockets.remove(websocket);
        -	// }
        -	// }
        -	//
        -	// protected Optional getSessionFromWebsocket(WebSocket websocket) {
        -	// return Optional.ofNullable(this.websockets.get(websocket));
        -	// }
        -	//
        -	// protected Optional getWebsocketFromSession(S session) {
        -	// return Optional.ofNullable(this.websockets.inverse().get(session));
        -	// }
         	/**
         	 * Send a message to a websocket
         	 *
         	 * @param j
         	 * @return true if successful, otherwise false
         	 */
        +	@Deprecated
         	public boolean send(WebSocket websocket, JsonObject j) {
        -		// System.out.println("SEND: websocket["+websocket+"]: " + j.toString());
         		try {
         			websocket.send(j.toString());
         			return true;
        @@ -202,4 +122,24 @@ public boolean send(WebSocket websocket, JsonObject j) {
         			return false;
         		}
         	}
        +	
        +	/**
        +	 * Get field from cookie in the handshake
        +	 *
        +	 * @param handshake
        +	 * @return value as optional
        +	 */
        +	protected Optional getFieldFromHandshakeCookie(ClientHandshake handshake, String fieldname) {
        +		JsonObject jCookie = new JsonObject();
        +		if (handshake.hasFieldValue("cookie")) {
        +			String cookieString = handshake.getFieldValue("cookie");
        +			for (String cookieVariable : cookieString.split("; ")) {
        +				String[] keyValue = cookieVariable.split("=");
        +				if (keyValue.length == 2) {
        +					jCookie.addProperty(keyValue[0], keyValue[1]);
        +				}
        +			}
        +		}
        +		return JsonUtils.getAsOptionalString(jCookie, fieldname);
        +	}
         }
        diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java
        index aeda9de7f51..5871a99e1c1 100644
        --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java
        +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java
        @@ -44,7 +44,7 @@ private static JsonObject newMessage(JsonObject jMessageId) {
         	 *			mode: "allow",
         	 *			token: String
         	 *		}, metadata: {
        -	 *			devices: [{
        +	 *			edges: [{
         	 *				id: number
         	 *				name: String,
         	 *				comment: String,
        @@ -54,20 +54,14 @@ private static JsonObject newMessage(JsonObject jMessageId) {
         	 *			}]
         	 *		}
         	 *	}
        -	 * - authenticate.role is only sent for OpenEMS Edge
        -	 * - metadata.devices is only sent for OpenEMS Backend
         	 * 
        * * @param token * @return */ - public static JsonObject browserConnectionSuccessfulReply(String token, Optional roleOpt, - JsonArray jEdges) { + public static JsonObject uiConnectionSuccessfulReply(String token, JsonArray jEdges) { JsonObject jAuthenticate = new JsonObject(); jAuthenticate.addProperty("mode", "allow"); - if (roleOpt.isPresent()) { - jAuthenticate.addProperty("role", roleOpt.get()); - } jAuthenticate.addProperty("token", token); JsonObject j = new JsonObject(); j.add("authenticate", jAuthenticate); diff --git a/ui/src/app/shared/service/websocket.ts b/ui/src/app/shared/service/websocket.ts index 75b464afda7..e44cc2222f8 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -167,24 +167,6 @@ export class Websocket { this.service.setToken(message.authenticate.token); } - if ("role" in message.authenticate && env.backend === "OpenEMS Edge") { - // for OpenEMS Edge we have only one device - let role = Role.getRole(message.authenticate.role); - let replyStream: { [messageId: string]: Subject } = {}; - this.replyStreams[Websocket.DEFAULT_DEVICENAME] = replyStream; - this.devices.next({ - fems: new Device(Websocket.DEFAULT_EDGEID, Websocket.DEFAULT_DEVICENAME, "FEMS", "", role, true, replyStream, this) - }); - } - - // TODO username is deprecated - // if ("username" in message.authenticate) { - // this.username = message.authenticate.username; - // this.event.next({ type: "success", message: this.service.translate.instant('Notifications.LoggedInAs', { value: this.username }) }); - // } else { - // this.event.next({ type: "success", message: this.service.translate.instant('Notifications.LoggedIn') }); - // } - } else { // authentication denied -> close websocket this.status = "failed"; diff --git a/ui/src/environments/environment.ts b/ui/src/environments/environment.ts index 7d980e24d10..0a8e48d4c13 100644 --- a/ui/src/environments/environment.ts +++ b/ui/src/environments/environment.ts @@ -3,8 +3,12 @@ import { DefaultTypes } from "../app/shared/service/defaulttypes"; class DefaultEnvironment extends Environment { public readonly production = false; - public readonly url = "ws://" + location.hostname + ":8088"; + // For OpenEMS Edge + public readonly url = "ws://" + location.hostname + ":8085"; public readonly backend: DefaultTypes.Backend = "OpenEMS Edge"; + // For OpenEMS Backend + // public readonly url = "ws://" + location.hostname + ":8088"; + // public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; public debugMode = true; } From d50da0a9a9e92a20f6283d75e0180e3c41433327 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Thu, 1 Mar 2018 22:53:37 +0100 Subject: [PATCH 112/156] Cleaning & TODOs --- cnf/central.xml | 49 +++++++++---------- .../io/openems/api/controller/Controller.java | 2 +- .../backend/application/BackendApp.java | 2 +- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/cnf/central.xml b/cnf/central.xml index 3b62d3b8cec..3137afbea82 100644 --- a/cnf/central.xml +++ b/cnf/central.xml @@ -9,31 +9,6 @@ pom - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.influxdb-java - 2.3_1 - - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.okhttp - 2.7.5_1 - - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.okio - 1.13.0_1 - - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.retrofit - 1.9.0_1 - - ch.qos.logback logback-classic @@ -63,14 +38,34 @@ org.apache.servicemix.bundles - org.apache.servicemix.bundles.xmlrpc-client - 3.1.3_1 + org.apache.servicemix.bundles.influxdb-java + 2.3_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okhttp + 2.7.5_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okio + 1.13.0_1 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.retrofit + 1.9.0_1 org.apache.servicemix.bundles org.apache.servicemix.bundles.ws-commons-util 1.0.2_2 + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.xmlrpc-client + 3.1.3_1 + org.java-websocket Java-WebSocket diff --git a/edge/src/io/openems/api/controller/Controller.java b/edge/src/io/openems/api/controller/Controller.java index acc7b147d88..e48cf4ce34f 100644 --- a/edge/src/io/openems/api/controller/Controller.java +++ b/edge/src/io/openems/api/controller/Controller.java @@ -45,7 +45,7 @@ public abstract class Controller implements Thing { @ChannelInfo(title = "Priority of this controller", type = Integer.class) public final ConfigChannel priority = new ConfigChannel("priority", this); - // TODO add "active" configchannel to be able to deactivate a controller without deleting it + // TODO add "enabled" configchannel to be able to deactivate a controller without deleting it public Controller() { this(null); diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index fdadb0e3170..00435e575de 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -61,7 +61,7 @@ public class BackendApp { @Activate void activate() { configureLogging(); - + // TODO implement MessageManager to decouple components log.debug("Activate BackendApp"); } From 0f7025f71dec244e8d3b28d31153fa949ccb3896 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 2 Mar 2018 14:34:11 +0100 Subject: [PATCH 113/156] Fix login/logout via username/password/token - Enable multiple tabs. - On logout all tabs are logged out. --- .../openems/api/persistence/Persistence.java | 68 ++--- .../websocket/EdgeCurrentDataWorker.java | 2 +- .../websocket/EdgeWebsocketHandler.java | 65 +++-- .../api/websocket/UiEdgeWebsocketHandler.java | 27 +- .../api/websocket/WebsocketApiServer.java | 274 +++++++++++------- .../session/WebsocketApiSessionManager.java | 2 +- .../fenecon/ReconnectingWebsocket.java | 3 +- ui/src/app/shared/service/websocket.ts | 1 + 8 files changed, 256 insertions(+), 186 deletions(-) diff --git a/edge/src/io/openems/api/persistence/Persistence.java b/edge/src/io/openems/api/persistence/Persistence.java index c60cc8b0775..b46500acc91 100644 --- a/edge/src/io/openems/api/persistence/Persistence.java +++ b/edge/src/io/openems/api/persistence/Persistence.java @@ -1,34 +1,34 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.api.persistence; - -import io.openems.api.thing.Thing; -import io.openems.core.utilities.AbstractWorker; - -public abstract class Persistence extends AbstractWorker implements Thing { - - public final static String THINGID_PREFIX = "_persistence"; - private static int instanceCounter = 0; - - public Persistence() { - super(THINGID_PREFIX + instanceCounter++); - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.api.persistence; + +import io.openems.api.thing.Thing; +import io.openems.core.utilities.AbstractWorker; + +public abstract class Persistence extends AbstractWorker implements Thing { + + public final static String THINGID_PREFIX = "_persistence"; + private static int instanceCounter = 0; + + public Persistence() { + super(THINGID_PREFIX + instanceCounter++); + } +} diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java index b226f217aec..2f70517cfca 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java @@ -28,7 +28,7 @@ public class EdgeCurrentDataWorker extends CurrentDataWorker { public EdgeCurrentDataWorker(EdgeWebsocketHandler edgeWebsocketHandler, JsonObject jMessageId, HashMultimap channels, Role role) { // TODO make sure websocket is present - super(edgeWebsocketHandler.getWebsocket().get(), jMessageId, channels); + super(edgeWebsocketHandler.getWebsocket(), jMessageId, channels); this.role = role; this.thingRepository = ThingRepository.getInstance(); this.databus = Databus.getInstance(); diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java index 4afe4a2473b..576eee3aceb 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java @@ -39,6 +39,7 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.WriteChannel; import io.openems.api.persistence.QueryablePersistence; +import io.openems.api.security.User; import io.openems.backend.timedata.api.TimedataService; import io.openems.common.exceptions.AccessDeniedException; import io.openems.common.exceptions.OpenemsException; @@ -70,7 +71,7 @@ public class EdgeWebsocketHandler { /** * Holds the websocket connection */ - protected Optional websocketOpt = Optional.empty(); + protected final WebSocket websocket; /** * Holds subscribers to current data @@ -83,40 +84,37 @@ public class EdgeWebsocketHandler { private final Map logSubscribers = new HashMap<>(); /** - * Predefined role for this connection. If empty, role is taken from message (in onMessage method). + * User for this connection. */ - private final Optional roleOpt; + private Optional userOpt = Optional.empty(); /** * ApiWorker handles updates to WriteChannels */ - private final Optional apiWorkerOpt; + private Optional apiWorkerOpt = Optional.empty(); + @Deprecated public EdgeWebsocketHandler() { - this.apiWorkerOpt = Optional.empty(); - this.roleOpt = Optional.empty(); + this.websocket = null; } - public EdgeWebsocketHandler(ApiWorker apiWorker, WebSocket websocket, Role role) { + public EdgeWebsocketHandler(WebSocket websocket, ApiWorker apiWorker) { + this.websocket = websocket; this.apiWorkerOpt = Optional.ofNullable(apiWorker); - this.websocketOpt = Optional.ofNullable(websocket); - this.roleOpt = Optional.ofNullable(role); } public void dispose() { // TODO dispose everything - if(this.websocketOpt.isPresent()) { - log.info("Dispose: Closing websocket"); - this.websocketOpt.get().close(); - } + log.info("Dispose: Closing websocket"); + this.websocket.close(); } - public void setWebsocket(WebSocket websocket) { - this.websocketOpt = Optional.ofNullable(websocket); + public void setUser(User user) { + this.userOpt = Optional.ofNullable(user); } - public Optional getWebsocket() { - return websocketOpt; + public Optional getUser() { + return userOpt; } /** @@ -128,11 +126,10 @@ public final void onMessage(JsonObject jMessage) { // get MessageId from message -> used for reply Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "messageId"); - // get role - // TODO + // get role Role role; - if (this.roleOpt.isPresent()) { - role = this.roleOpt.get(); + if (this.userOpt.isPresent()) { + role = this.userOpt.get().getRole(); } else { Optional roleStringOpt = JsonUtils.getAsOptionalString(jMessage, "role"); if (roleStringOpt.isPresent()) { @@ -230,7 +227,7 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional configChannel = (ConfigChannel) channel; Object value = ConfigUtils.getConfigObject(configChannel, jValue); configChannel.updateValue(value, true); - WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, jMessageId, + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue); @@ -269,23 +266,23 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional writeChannel = (WriteChannel) channel; if (!apiWorkerOpt.isPresent()) { - WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, new JsonObject() /* TODO */, + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, Notification.BACKEND_NOT_ALLOWED, "set " + channel.address() + " => " + jValue); } else { ApiWorker apiWorker = apiWorkerOpt.get(); WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> { - WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS, "set " + channel.address() + " => " + jValue); }).onFirstError((e) -> { - WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_FAILED, "set " + channel.address() + " => " + jValue, e.getMessage()); }).onTimeout(() -> { - WebSocketUtils.sendNotificationOrLogError(this.websocketOpt, + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, "set " + channel.address() + " => " + jValue); @@ -297,7 +294,7 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional handlers = new HashMap<>(); + + /** + * Stores valid session tokens for authentication via Cookie (this maps to a browser window) + */ + private final Map sessionTokens = new HashMap<>(); + + /** + * Stores handlers per websocket (this maps to a browser tab). + * The handler lives while the websocket is connected. Independently of the login/logout state. + */ + private final Map handlers = new HashMap<>(); private final ApiWorker apiWorker; private final static int TOKEN_LENGTH = 130; @@ -63,16 +74,36 @@ public WebsocketApiServer(ApiWorker apiWorker, int port) { */ @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { + // generate UUID for this websocket (browser tab) + UUID uuid = UUID.randomUUID(); + + // get token from cookie or generate new token + String token; + Optional cookieTokenOpt = getFieldFromHandshakeCookie(handshake, "token"); + if (cookieTokenOpt.isPresent()) { + token = cookieTokenOpt.get(); + } else { + // Generate token (source: http://stackoverflow.com/a/41156) + SecureRandom sr = SecureRandomSingleton.getInstance(); + token = new BigInteger(TOKEN_LENGTH, sr).toString(32); + } + + // create new Handler and store it + UiEdgeWebsocketHandler handler = new UiEdgeWebsocketHandler(websocket, apiWorker, token, uuid); + this.handlers.put(uuid, handler); + websocket.setAttachment(uuid); + // login using token from the cookie - Optional tokenOpt = getFieldFromHandshakeCookie(handshake, "token"); - if (tokenOpt.isPresent()) { - String token = tokenOpt.get(); - UiEdgeWebsocketHandler handler = this.handlers.get(token); - if (handler != null) { + if (cookieTokenOpt.isPresent()) { + User user = this.sessionTokens.get(token); + if (user != null) { + /* + * token from cookie is valid -> authentication successful + */ + // send reply and log try { - // Authentication successful - this.handleAuthenticationSuccessful(handler.getUser(), websocket); - log.info("User [" + getUserName(websocket) + "] logged in by token"); + this.handleAuthenticationSuccessful(handler, user); + log.info("User [" + user.getName() + "] logged in by token"); return; } catch (OpenemsException e) { // TODO handle error @@ -83,51 +114,11 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { // if we are here, automatic authentication was not possible -> notify client WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */, - LogBehaviour.WRITE_TO_LOG, Notification.EDGE_AUTHENTICATION_BY_TOKEN_FAILED, tokenOpt.orElse("")); - } - - @Override - protected void _onMessage(WebSocket websocket, JsonObject jMessage) { - /* - * Authenticate - */ - Optional jAuthenticateOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "authenticate"); - if (jAuthenticateOpt.isPresent()) { - // authenticate by username/password - try { - authenticate(jAuthenticateOpt.get(), websocket); - } catch (OpenemsException e) { - // TODO error - log.error(e.getMessage()); - } - return; - } - - // get current Token - String token = websocket.getAttachment(); - if (token == null) { - // TODO error: no token - return; - } - UiEdgeWebsocketHandler handler = this.handlers.get(token); - if (handler == null) { - // TODO error: no handler - return; - } - - /* - * Rest -> forward to websocket handler - */ - handler.onMessage(jMessage); - } - - @Override - protected void _onClose(WebSocket websocket) { - log.info("User [" + getUserName(websocket) + "] closed websocket connection"); + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_AUTHENTICATION_BY_TOKEN_FAILED, cookieTokenOpt.orElse("")); } /** - * Authenticates a user according to the "authenticate" message. Stores and returnes the Role if valid. + * Authenticates a user according to the "authenticate" message. Stores the User if valid. * * @param jAuthenticateElement * @param handler @@ -156,8 +147,9 @@ private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws } // authentication successful User user = userOpt.get(); - log.info("User [" + user.getName() + "] logged in by username/password"); - this.handleAuthenticationSuccessful(user, websocket); + UiEdgeWebsocketHandler handler = getHandlerOrCloseWebsocket(websocket); + this.sessionTokens.put(handler.getSessionToken(), user); + this.handleAuthenticationSuccessful(handler, user); } catch (OpenemsException e) { /* @@ -166,6 +158,7 @@ private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws JsonObject jReply = DefaultMessages.uiConnectionFailedReply(); WebSocketUtils.send(websocket, jReply); websocket.closeConnection(CloseFrame.REFUSE, "Error while authenticating: " + e.getMessage()); + log.error(e.getMessage()); return; } break; @@ -173,25 +166,98 @@ private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws /* * Logout and close session */ - String token = websocket.getAttachment(); - if (token != null) { - UiEdgeWebsocketHandler handler = this.handlers.get(token); - if (handler != null) { - handler.dispose(); + String sessionToken = "none"; + String username = "UNKNOWN"; + try { + UiEdgeWebsocketHandler handler = this.getHandlerOrCloseWebsocket(websocket); + if(handler.getUser().isPresent()) { + username = handler.getUser().get().getName(); + } + sessionToken = handler.getSessionToken(); + this.sessionTokens.remove(sessionToken); + log.info("User [" + username + "] logged out. Invalidated token [" + sessionToken + "]"); + + // find and close all websockets for this user + if(handler.getUser().isPresent()) { + User thisUser = handler.getUser().get(); + for(UiEdgeWebsocketHandler h : this.handlers.values()) { + if(h.getUser().isPresent()) { + User otherUser = h.getUser().get(); + if(otherUser.equals(thisUser)) { + // TODO send notification "user was logged out" + h.getWebsocket().close(); + } + } + } } - this.handlers.remove(token); - log.info("User [" + getUserName(websocket) + "] logged out. Invalidated token [" + token + "]"); // TODO send notification - } else { - log.warn("User tries to log out, but was not logged in."); + } catch (OpenemsException e) { + log.error("Unable to close session [" + sessionToken + "]: " + e.getMessage()); + // TODO send notification } - websocket.setAttachment(null); } } } - private void handleAuthenticationSuccessful(User user, WebSocket websocket) throws OpenemsException { + @Override + protected void _onMessage(WebSocket websocket, JsonObject jMessage) { + /* + * Authenticate + */ + Optional jAuthenticateOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "authenticate"); + if (jAuthenticateOpt.isPresent()) { + // authenticate by username/password + try { + authenticate(jAuthenticateOpt.get(), websocket); + } catch (OpenemsException e) { + // TODO error + log.error(e.getMessage()); + } + return; + } + + // get handler + UiEdgeWebsocketHandler handler; + try { + handler = getHandlerOrCloseWebsocket(websocket); + } catch (OpenemsException e) { + log.error("onMessage Error: " + e.getMessage()); + return; + } + + // get session Token from handler + String token = handler.getSessionToken(); + if (!this.sessionTokens.containsKey(token)) { + // TODO error: token is not anymore valid. + log.error("Token [" + token + "] is not anymore valid."); + websocket.close(); + return; + } + + // From here authentication was successful + + /* + * Rest -> forward to websocket handler + */ + handler.onMessage(jMessage); + } + + @Override + protected void _onError(WebSocket websocket, Exception ex) { + log.warn("User [" + getUserName(websocket) + "] error: " + ex.getMessage()); + } + + @Override + protected void _onClose(WebSocket websocket) { + log.info("User [" + getUserName(websocket) + "] closed websocket connection"); + this.disposeHandler(websocket); + } + + private void handleAuthenticationSuccessful(UiEdgeWebsocketHandler handler, User user) throws OpenemsException { + // add user to handler + handler.setUser(user); + // Create Edges entry JsonObject jEdge = new JsonObject(); jEdge.addProperty("id", 0); @@ -203,63 +269,55 @@ private void handleAuthenticationSuccessful(User user, WebSocket websocket) thro JsonArray jEdges = new JsonArray(); jEdges.add(jEdge); - // invalidate old handler if exists - String token = websocket.getAttachment(); - if (token != null) { - UiEdgeWebsocketHandler oldHandler = this.handlers.get(token); - oldHandler.dispose(); - this.handlers.remove(token); - } else { - // Generate token (source: http://stackoverflow.com/a/41156) - SecureRandom sr = SecureRandomSingleton.getInstance(); - token = new BigInteger(TOKEN_LENGTH, sr).toString(32); - } - - // create new Handler - UiEdgeWebsocketHandler handler = new UiEdgeWebsocketHandler(token, user); - - // save - this.handlers.put(token, handler); - websocket.setAttachment(token); - // send reply - JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply(token, jEdges); - WebSocketUtils.send(websocket, jReply); - } - - @Override - protected void _onError(WebSocket websocket, Exception ex) { - log.warn("User [" + getUserName(websocket) + "] error: " + ex.getMessage()); + JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply(handler.getSessionToken(), jEdges); + handler.send(jReply); } - /** - * Get token from handshake - * - * @param handshake - * @return cookie as JsonObject - */ - private Optional getTokenFromHandshake(ClientHandshake handshake) { - if (handshake.hasFieldValue("token")) { - return Optional.ofNullable(handshake.getFieldValue("token")); + public void sendLog(long timestamp, String level, String source, String message) { + for (UiEdgeWebsocketHandler handler : this.handlers.values()) { + handler.sendLog(timestamp, level, source, message); } - return Optional.empty(); } private String getUserName(WebSocket websocket) { - String token = websocket.getAttachment(); - if (token != null) { - UiEdgeWebsocketHandler handler = this.handlers.get(token); - if(handler != null) { - User user = handler.getUser(); + Optional handlerOpt = getHandlerOpt(websocket); + if (handlerOpt.isPresent()) { + UiEdgeWebsocketHandler handler = handlerOpt.get(); + if(handler.getUser().isPresent()) { + User user = handler.getUser().get(); return user.getName(); } } return "UNKNOWN"; } - public void sendLog(long timestamp, String level, String source, String message) { - for(UiEdgeWebsocketHandler handler : this.handlers.values()) { - handler.sendLog(timestamp, level, source, message); + private Optional getHandlerOpt(WebSocket websocket) { + UUID uuid = websocket.getAttachment(); + return Optional.ofNullable(this.handlers.get(uuid)); + } + + private UiEdgeWebsocketHandler getHandlerOrCloseWebsocket(WebSocket websocket) throws OpenemsException { + Optional handlerOpt = getHandlerOpt(websocket); + UUID uuid = websocket.getAttachment(); + UiEdgeWebsocketHandler handler = this.handlers.get(uuid); + if (!handlerOpt.isPresent()) { + // no handler! close websocket + websocket.close(); // TODO error message + notification + throw new OpenemsException("Websocket had no Handler. Closing websocket."); + } + return handler; + } + + private void disposeHandler(WebSocket websocket) { + UiEdgeWebsocketHandler handler; + try { + handler = getHandlerOrCloseWebsocket(websocket); + UUID uuid = handler.getUuid(); + this.handlers.remove(uuid); + handler.dispose(); + } catch (OpenemsException e) { + log.warn("Unable to dispose Handler: " + e.getMessage()); } } } diff --git a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java b/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java index b0e22c2f918..ce06f701af5 100644 --- a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java +++ b/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java @@ -25,7 +25,7 @@ private Optional createSessionForUser(Optional userOp if (userOpt.isPresent()) { User user = userOpt.get(); WebsocketApiSessionData data = new WebsocketApiSessionData(user, - new EdgeWebsocketHandler(apiWorker, websocket, user.getRole())); + new EdgeWebsocketHandler(websocket, apiWorker)); String token = generateToken(); WebsocketApiSession session = createNewSession(token, data); return Optional.of(session); diff --git a/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java b/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java index 7f526d306e3..952f1ee8706 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java +++ b/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java @@ -172,7 +172,8 @@ public ReconnectingWebsocket(EdgeWebsocketHandler handler, OnOpenListener onOpen } ws.connect(); WEBSOCKET_OPT = Optional.of(ws); - WEBSOCKET_HANDLER.setWebsocket(ws); + // TODO: websocket cannot be changed + // WEBSOCKET_HANDLER.setWebsocket(ws); } catch (Throwable t) { String wsString = uriOpt.isPresent() ? uriOpt.get().toString() : "NO_URI"; diff --git a/ui/src/app/shared/service/websocket.ts b/ui/src/app/shared/service/websocket.ts index e44cc2222f8..6a901e3fe12 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -273,6 +273,7 @@ export class Websocket { * Reset everything to default */ private initialize() { + // TODO do not stop the websocket connection on logout this.stopOnInitialize.next(); this.stopOnInitialize.complete(); this.messageSubscription.unsubscribe(); From 8e5f6fd01b871290049c3b1ef29963c7c142750a Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 2 Mar 2018 19:13:32 +0100 Subject: [PATCH 114/156] Fix currentData, historicData and log for local UI --- .../websocket/EdgeCurrentDataWorker.java | 9 +- .../websocket/EdgeWebsocketHandler.java | 201 +++++++++--------- .../api/websocket/WebsocketApiServer.java | 24 ++- .../influxdb/InfluxdbPersistence.java | 14 +- .../backend/timedata/api/TimedataService.java | 50 ++++- .../backend/timedata/influx/Influx.java | 11 +- .../timedata/influx/InfluxdbUtils.java | 8 +- .../provider/BackendCurrentDataWorker.java | 7 +- .../impl/provider/UiWebsocketServer.java | 51 +---- .../common/websocket/CurrentDataWorker.java | 65 ++++-- 10 files changed, 245 insertions(+), 195 deletions(-) diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java index 2f70517cfca..bb42461c359 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java @@ -2,9 +2,9 @@ import java.util.Optional; -import com.google.common.collect.HashMultimap; +import org.java_websocket.WebSocket; + import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import io.openems.api.channel.Channel; import io.openems.common.exceptions.AccessDeniedException; @@ -26,9 +26,8 @@ public class EdgeCurrentDataWorker extends CurrentDataWorker { */ private final Role role; - public EdgeCurrentDataWorker(EdgeWebsocketHandler edgeWebsocketHandler, JsonObject jMessageId, HashMultimap channels, Role role) { - // TODO make sure websocket is present - super(edgeWebsocketHandler.getWebsocket(), jMessageId, channels); + public EdgeCurrentDataWorker(EdgeWebsocketHandler edgeWebsocketHandler, WebSocket websocket, Role role) { + super(websocket); this.role = role; this.thingRepository = ThingRepository.getInstance(); this.databus = Databus.getInstance(); diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java index 576eee3aceb..f7d8829dfc5 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java @@ -30,7 +30,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.HashMultimap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -45,7 +44,6 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; -import io.openems.common.websocket.CurrentDataWorker; import io.openems.common.websocket.DefaultMessages; import io.openems.common.websocket.LogBehaviour; import io.openems.common.websocket.Notification; @@ -74,9 +72,9 @@ public class EdgeWebsocketHandler { protected final WebSocket websocket; /** - * Holds subscribers to current data + * Holds subscriber to current data */ - private final HashMap currentDataSubscribers = new HashMap<>(); + private Optional currentDataWorkerOpt = Optional.empty(); /** * Holds subscribers to system log (identified by messageId.backend, holds complete jMessageId) @@ -103,17 +101,36 @@ public EdgeWebsocketHandler(WebSocket websocket, ApiWorker apiWorker) { this.apiWorkerOpt = Optional.ofNullable(apiWorker); } - public void dispose() { - // TODO dispose everything - log.info("Dispose: Closing websocket"); + public synchronized void dispose() { + if (this.currentDataWorkerOpt.isPresent()) { + this.currentDataWorkerOpt.get().dispose(); + this.currentDataWorkerOpt = Optional.empty(); + } + this.logSubscribers.clear(); this.websocket.close(); } - public void setUser(User user) { - this.userOpt = Optional.ofNullable(user); + public synchronized void setUser(User user) { + this.setUser(Optional.ofNullable(user)); + } + + public synchronized void unsetUser() { + this.setUser(Optional.empty()); } - public Optional getUser() { + public synchronized void setUser(Optional userOpt) { + this.userOpt = userOpt; + if (this.currentDataWorkerOpt.isPresent()) { + this.currentDataWorkerOpt.get().dispose(); + this.currentDataWorkerOpt = Optional.empty(); + } + if (userOpt.isPresent()) { + Role role = userOpt.get().getRole(); + this.currentDataWorkerOpt = Optional.of(new EdgeCurrentDataWorker(this, websocket, role)); + } + } + + public Optional getUserOpt() { return userOpt; } @@ -123,21 +140,28 @@ public Optional getUser() { * @param jMessage */ public final void onMessage(JsonObject jMessage) { + if (!this.userOpt.isPresent()) { + log.error("No User! Aborting..."); + return; + } + Role role = this.userOpt.get().getRole(); + // get MessageId from message -> used for reply Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "messageId"); - // get role - Role role; - if (this.userOpt.isPresent()) { - role = this.userOpt.get().getRole(); - } else { - Optional roleStringOpt = JsonUtils.getAsOptionalString(jMessage, "role"); - if (roleStringOpt.isPresent()) { - role = Role.getRole(roleStringOpt.get()); - } else { - role = Role.getDefaultRole(); - } - } + // get role + // TODO + // Role role; + // if (this.userOpt.isPresent()) { + // role = this.userOpt.get().getRole(); + // } else { + // Optional roleStringOpt = JsonUtils.getAsOptionalString(jMessage, "role"); + // if (roleStringOpt.isPresent()) { + // role = Role.getRole(roleStringOpt.get()); + // } else { + // role = Role.getDefaultRole(); + // } + // } if (jMessageIdOpt.isPresent()) { JsonObject jMessageId = jMessageIdOpt.get(); @@ -155,7 +179,7 @@ public final void onMessage(JsonObject jMessage) { */ Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); if (jCurrentDataOpt.isPresent()) { - currentData(role, jMessageId, jCurrentDataOpt.get()); + currentData(jMessageId, jCurrentDataOpt.get()); return; } @@ -164,22 +188,7 @@ public final void onMessage(JsonObject jMessage) { */ Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); if (jhistoricDataOpt.isPresent()) { - // select first QueryablePersistence (by default the running InfluxdbPersistence) - TimedataService timedataSource = null; - for (QueryablePersistence queryablePersistence : ThingRepository.getInstance() - .getQueryablePersistences()) { - timedataSource = queryablePersistence; - break; - } - if (timedataSource == null) { - // TODO create notification that there is no datasource available - } else { - // TODO - // jReply = JsonUtils.merge(jReply, // - // WebSocketUtils.historicData(jIdOpt.get(), jhistoricDataOpt.get(), deviceIdOpt, timedataSource, - // role) // - // ); - } + historicData(jMessageId, jhistoricDataOpt.get()); return; } @@ -189,7 +198,7 @@ public final void onMessage(JsonObject jMessage) { Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log"); if (jLogOpt.isPresent()) { try { - log(jMessageId, jLogOpt.get(), role); + log(role, jMessageId, jLogOpt.get()); } catch (OpenemsException e) { log.error(e.getMessage()); } @@ -210,6 +219,28 @@ public final void onMessage(JsonObject jMessage) { } } + private void historicData(JsonObject jMessageId, JsonObject jHistoricData) { + // select first QueryablePersistence (by default the running InfluxdbPersistence) + TimedataService timedataSource = null; + for (QueryablePersistence queryablePersistence : ThingRepository.getInstance().getQueryablePersistences()) { + timedataSource = queryablePersistence; + break; + } + if (timedataSource == null) { + // TODO create notification that there is no datasource available + return; + } + JsonArray jData; + try { + jData = timedataSource.queryHistoricData(jHistoricData); + WebSocketUtils.send(this.websocket, DefaultMessages.historicDataQueryReply(jMessageId, jData)); + } catch (OpenemsException e) { + // TODO notification + log.error(e.getMessage()); + } + return; + } + /** * Handle "config" messages * @@ -256,9 +287,8 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional configChannel = (ConfigChannel) channel; Object value = ConfigUtils.getConfigObject(configChannel, jValue); configChannel.updateValue(value, true); - WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, - LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS, - channel.address() + " => " + jValue); + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_CHANNEL_UPDATE_SUCCESS, channel.address() + " => " + jValue); } else if (channel instanceof WriteChannel) { /* @@ -266,25 +296,22 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional writeChannel = (WriteChannel) channel; if (!apiWorkerOpt.isPresent()) { - WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject() /* TODO */, + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, Notification.BACKEND_NOT_ALLOWED, "set " + channel.address() + " => " + jValue); } else { ApiWorker apiWorker = apiWorkerOpt.get(); WriteObject writeObject = new WriteJsonObject(jValue).onFirstSuccess(() -> { - WebSocketUtils.sendNotificationOrLogError(this.websocket, - new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_SUCCESS, + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_SUCCESS, "set " + channel.address() + " => " + jValue); }).onFirstError((e) -> { - WebSocketUtils.sendNotificationOrLogError(this.websocket, - new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_FAILED, + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_FAILED, "set " + channel.address() + " => " + jValue, e.getMessage()); }).onTimeout(() -> { - WebSocketUtils.sendNotificationOrLogError(this.websocket, - new JsonObject() /* TODO */, LogBehaviour.WRITE_TO_LOG, - Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, + LogBehaviour.WRITE_TO_LOG, Notification.EDGE_CHANNEL_UPDATE_TIMEOUT, "set " + channel.address() + " => " + jValue); }); apiWorker.addValue(writeChannel, writeObject); @@ -307,37 +334,19 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional channels = HashMultimap.create(); JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); - for (Entry entry : jSubscribeChannels.entrySet()) { - String thing = entry.getKey(); - JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement jChannel : jChannels) { - String channel = JsonUtils.getAsString(jChannel); - channels.put(thing, channel); - } - } - if (!channels.isEmpty()) { - // create new worker - worker = new EdgeCurrentDataWorker(this, jMessageId, channels, role); - this.currentDataSubscribers.put(messageIdUi, worker); - } + this.currentDataWorkerOpt.get().setChannels(jSubscribeChannels, jMessageId); } } catch (OpenemsException e) { log.warn(e.getMessage()); @@ -351,25 +360,35 @@ private synchronized JsonObject currentData(Role role, JsonObject jMessageId, Js * @param j * @throws AccessDeniedException */ - private synchronized void log(JsonObject jMessageId, JsonObject jLog, Role role) throws OpenemsException { - if (!(role == Role.ADMIN || role == Role.INSTALLER || role == Role.OWNER)) { + private synchronized void log(Role role, JsonObject jMessageId, JsonObject jLog) throws OpenemsException { + // check permissions + switch (role) { + case ADMIN: + case INSTALLER: + case OWNER: + /* allowed */ + break; + case GUEST: + default: throw new AccessDeniedException("User role [" + role + "] is not allowed to read system logs."); } + String mode = JsonUtils.getAsString(jLog, "mode"); - String messageIdBackend = JsonUtils.getAsString(jMessageId, "backend"); + String messageId = JsonUtils.getAsOptionalString(jMessageId, "backend") + .orElse(JsonUtils.getAsString(jMessageId, "ui")); if (mode.equals("subscribe")) { /* * Subscribe to system log */ - log.info("UI [" + messageIdBackend + "] subscribed to log..."); - this.logSubscribers.put(messageIdBackend, jMessageId); + log.info("UI [" + messageId + "] subscribed to log."); + this.logSubscribers.put(messageId, jMessageId); } else if (mode.equals("unsubscribe")) { /* * Unsubscribe from system log */ - log.info("UI [" + messageIdBackend + "] unsubscribed from log..."); - this.logSubscribers.remove(messageIdBackend); + log.info("UI [" + messageId + "] unsubscribed from log."); + this.logSubscribers.remove(messageId); } } @@ -612,24 +631,6 @@ private synchronized void system(JsonObject jMessageId, JsonObject jSystem, Role // } // } - /** - * Send a notification message/error to the websocket - * - * @param mesage - * @return true if successful, otherwise false - */ - // TODO send notification - // public synchronized void sendNotification(NotificationType type, String message) { - // JsonObject jNotification = new JsonObject(); - // jNotification.addProperty("type", type.name().toLowerCase()); - // jNotification.addProperty("message", message); - // JsonObject j = new JsonObject(); - // j.add("notification", jNotification); - // new Thread(() -> { - // WebSocketUtils.send(websocket, j); - // }).start(); - // } - /** * Send a message to the websocket. * diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java index 84ce59e03d3..f34f5c074f3 100644 --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java @@ -170,22 +170,24 @@ private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws String username = "UNKNOWN"; try { UiEdgeWebsocketHandler handler = this.getHandlerOrCloseWebsocket(websocket); - if(handler.getUser().isPresent()) { - username = handler.getUser().get().getName(); + Optional thisUserOpt = handler.getUserOpt(); + if (thisUserOpt.isPresent()) { + username = thisUserOpt.get().getName(); + handler.unsetUser(); } sessionToken = handler.getSessionToken(); this.sessionTokens.remove(sessionToken); log.info("User [" + username + "] logged out. Invalidated token [" + sessionToken + "]"); // find and close all websockets for this user - if(handler.getUser().isPresent()) { - User thisUser = handler.getUser().get(); - for(UiEdgeWebsocketHandler h : this.handlers.values()) { - if(h.getUser().isPresent()) { - User otherUser = h.getUser().get(); - if(otherUser.equals(thisUser)) { + if (thisUserOpt.isPresent()) { + User thisUser = thisUserOpt.get(); + for (UiEdgeWebsocketHandler h : this.handlers.values()) { + if (h.getUserOpt().isPresent()) { + User otherUser = h.getUserOpt().get(); + if (otherUser.equals(thisUser)) { // TODO send notification "user was logged out" - h.getWebsocket().close(); + h.dispose(); } } } @@ -284,8 +286,8 @@ private String getUserName(WebSocket websocket) { Optional handlerOpt = getHandlerOpt(websocket); if (handlerOpt.isPresent()) { UiEdgeWebsocketHandler handler = handlerOpt.get(); - if(handler.getUser().isPresent()) { - User user = handler.getUser().get(); + if (handler.getUserOpt().isPresent()) { + User user = handler.getUserOpt().get(); return user.getName(); } } diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java index 86828de7ba4..3e8a6627367 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java @@ -42,9 +42,9 @@ import io.openems.api.channel.thingstate.ThingStateChannels; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; -import io.openems.common.exceptions.OpenemsException; import io.openems.api.persistence.QueryablePersistence; import io.openems.backend.timedata.influx.InfluxdbUtils; +import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelEnum; import io.openems.core.Databus; @@ -108,8 +108,8 @@ public void channelUpdated(Channel channel, Optional newValue) { fieldValue = new NumberFieldValue(field, (Number) value); } else if (value instanceof String) { fieldValue = new StringFieldValue(field, (String) value); - }else if (value instanceof ChannelEnum) { - fieldValue = new NumberFieldValue(field, ((ChannelEnum)value).getValue()); + } else if (value instanceof ChannelEnum) { + fieldValue = new NumberFieldValue(field, ((ChannelEnum) value).getValue()); } else { return; } @@ -204,8 +204,8 @@ private Optional getInfluxDB() { } @Override - public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, - JsonObject channels, int resolution) throws io.openems.common.exceptions.OpenemsException { + public JsonArray queryHistoricData(Optional edgeIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + int resolution) throws io.openems.common.exceptions.OpenemsException { Optional influxdbOpt = getInfluxDB(); if (!influxdbOpt.isPresent()) { throw new OpenemsException("InfluxDB is not available"); @@ -214,8 +214,8 @@ public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDate if (!databaseOpt.isPresent()) { throw new OpenemsException("InfluxDB database is not available"); } - return InfluxdbUtils.queryHistoricData(influxdbOpt.get(), databaseOpt.get(), edgeId, fromDate, toDate, - channels, resolution); + return InfluxdbUtils.queryHistoricData(influxdbOpt.get(), databaseOpt.get(), edgeIdOpt, fromDate, + toDate, channels, resolution); } @Override diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index 317f12576f2..ddc940c240b 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -1,5 +1,8 @@ package io.openems.backend.timedata.api; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Optional; @@ -10,6 +13,7 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; @ProviderType public interface TimedataService { @@ -39,7 +43,7 @@ public interface TimedataService { * @return */ public Optional getChannelValue(int edgeId, ChannelAddress channelAddress); - + /** * Queries the database and returns a JsonArray of the form * @@ -62,6 +66,46 @@ public interface TimedataService { * @return * @throws OpenemsException */ - public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, - int resolution/* , JsonObject kWh */) throws OpenemsException; + public JsonArray queryHistoricData(Optional edgeIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, + JsonObject channels, int resolution/* , JsonObject kWh */) throws OpenemsException; + + public default JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, + JsonObject channels, int resolution/* , JsonObject kWh */) throws OpenemsException { + return this.queryHistoricData(Optional.of(edgeId), fromDate, toDate, channels, resolution); + } + + public default JsonArray queryHistoricData(ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + int resolution/* , JsonObject kWh */) throws OpenemsException { + return this.queryHistoricData(Optional.empty(), fromDate, toDate, channels, resolution); + } + + public default JsonArray queryHistoricData(int edgeId, JsonObject jHistoricData) throws OpenemsException { + return this.queryHistoricData(Optional.of(edgeId), jHistoricData); + } + + public default JsonArray queryHistoricData(JsonObject jHistoricData) throws OpenemsException { + return this.queryHistoricData(Optional.empty(), jHistoricData); + } + + public default JsonArray queryHistoricData(Optional edgeIdOpt, JsonObject jHistoricData) + throws OpenemsException { + int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); + ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); + ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); + ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); + JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); + // TODO check if role is allowed to read these channels + // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); + int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); + // TODO: better calculation of sensible resolution + int resolution = 10 * 60; // 10 Minutes + if (days > 25) { + resolution = 24 * 60 * 60; // 1 Day + } else if (days > 6) { + resolution = 3 * 60 * 60; // 3 Hours + } else if (days > 2) { + resolution = 60 * 60; // 60 Minutes + } + return this.queryHistoricData(edgeIdOpt, fromDate, toDate, channels, resolution); + } } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index 924eef5ae12..fe51d566e0f 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -197,12 +197,15 @@ private void writeDataToOldMiniMonitoring(Edge edge, int influxId, TreeBasedTabl } @Override - public JsonArray queryHistoricData(int edgeId, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, + public JsonArray queryHistoricData(Optional edgeIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { - Edge edge = this.metadataService.getEdge(edgeId); - int influxId = InfluxdbUtils.parseNumberFromName(edge.getName()); + Optional influxIdOpt = Optional.empty(); // if given, query only this id. If not given, do not apply filter + if(edgeIdOpt.isPresent()) { + Edge edge = this.metadataService.getEdge(edgeIdOpt.get()); + influxIdOpt = Optional.of(InfluxdbUtils.parseNumberFromName(edge.getName())); + } InfluxDB influxDB = getInfluxDbConnection(); - return InfluxdbUtils.queryHistoricData(influxDB, this.database, influxId, fromDate, toDate, channels, + return InfluxdbUtils.queryHistoricData(influxDB, this.database, influxIdOpt, fromDate, toDate, channels, resolution); } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java index 2545b28078a..a9d31e18c55 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java @@ -35,13 +35,15 @@ public class InfluxdbUtils { private final static Logger log = LoggerFactory.getLogger(InfluxdbUtils.class); - public static JsonArray queryHistoricData(InfluxDB influxdb, String database, int influxId, ZonedDateTime fromDate, - ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { + public static JsonArray queryHistoricData(InfluxDB influxdb, String database, Optional influxIdOpt, + ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, int resolution) throws OpenemsException { // Prepare query string StringBuilder query = new StringBuilder("SELECT "); query.append(toChannelAddressList(channels)); query.append(" FROM data WHERE "); - query.append("fems = '" + influxId + "' AND "); + if (influxIdOpt.isPresent()) { + query.append("fems = '" + influxIdOpt.get() + "' AND "); + } query.append("time > "); query.append(String.valueOf(fromDate.toEpochSecond())); query.append("s"); diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java index f3bbbfc7f51..37458d8d38a 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/BackendCurrentDataWorker.java @@ -4,9 +4,7 @@ import org.java_websocket.WebSocket; -import com.google.common.collect.HashMultimap; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import io.openems.common.exceptions.NotImplementedException; import io.openems.common.types.ChannelAddress; @@ -18,9 +16,8 @@ public class BackendCurrentDataWorker extends CurrentDataWorker { private final UiWebsocketServer parent; private final int edgeId; - public BackendCurrentDataWorker(UiWebsocketServer parent, WebSocket websocket, JsonObject jMessageId, int edgeId, - HashMultimap channels) { - super(websocket, jMessageId, channels); + public BackendCurrentDataWorker(UiWebsocketServer parent, WebSocket websocket, int edgeId) { + super(websocket); this.parent = parent; this.edgeId = edgeId; } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 099dfa8ea5d..befe2656fa3 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -1,12 +1,12 @@ package io.openems.backend.uiwebsocket.impl.provider; -import java.util.Map.Entry; import java.time.Period; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.UUID; @@ -16,9 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.HashMultimap; import com.google.gson.JsonArray; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.backend.metadata.api.Edge; @@ -90,8 +88,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { } } log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); - JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply("" /* empty token? */, - jEdges); + JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply("" /* empty token? */, jEdges); WebSocketUtils.sendOrLogError(websocket, jReply); } @@ -203,7 +200,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { */ if (jMessage.has("config") || jMessage.has("log") || jMessage.has("system")) { try { - log.info("User [" + user.getName() + "] Forward message to Edge [" + edge.getName() +"]: " + StringUtils.toShortString(jMessage, 100)); + log.info("User [" + user.getName() + "] Forward message to Edge [" + edge.getName() + "]: " + + StringUtils.toShortString(jMessage, 100)); Optional roleOpt = user.getEdgeRole(edgeId); JsonObject j = DefaultMessages.prepareMessageForForwardToEdge(jMessage, data.getUuid(), roleOpt); this.parent.edgeWebsocketService.forwardMessageFromUi(edgeId, j); @@ -237,23 +235,11 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, J workerOpt.get().dispose(); } - // parse subscribed channels - HashMultimap channels = HashMultimap.create(); + // set new worker JsonObject jSubscribeChannels = JsonUtils.getAsJsonObject(jCurrentData, "channels"); - for (Entry entry : jSubscribeChannels.entrySet()) { - String thing = entry.getKey(); - JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); - for (JsonElement jChannel : jChannels) { - String channel = JsonUtils.getAsString(jChannel); - channels.put(thing, channel); - } - } - if (!channels.isEmpty()) { - // create new worker - BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, jMessageId, edgeId, - channels); - data.setCurrentDataWorker(worker); - } + BackendCurrentDataWorker worker = new BackendCurrentDataWorker(this, websocket, edgeId); + worker.setChannels(jSubscribeChannels, jMessageId); + data.setCurrentDataWorker(worker); } } catch (OpenemsException e) { // TODO handle exception @@ -274,26 +260,7 @@ private JsonObject historicData(JsonObject jMessageId, int edgeId, JsonObject jH /* * Query historic data */ - int timezoneDiff = JsonUtils.getAsInt(jHistoricData, "timezone"); - ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1)); - ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(jHistoricData, "fromDate", timezone); - ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(jHistoricData, "toDate", timezone).plusDays(1); - JsonObject channels = JsonUtils.getAsJsonObject(jHistoricData, "channels"); - // TODO check if role is allowed to read these channels - // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); - int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); - // TODO: better calculation of sensible resolution - int resolution = 10 * 60; // 10 Minutes - if (days > 25) { - resolution = 24 * 60 * 60; // 1 Day - } else if (days > 6) { - resolution = 3 * 60 * 60; // 3 Hours - } else if (days > 2) { - resolution = 60 * 60; // 60 Minutes - } - JsonArray jData = this.parent.timeDataService.queryHistoricData(edgeId, fromDate, toDate, channels, - resolution); - // send reply + JsonArray jData = this.parent.timeDataService.queryHistoricData(edgeId, jHistoricData); return DefaultMessages.historicDataQueryReply(jMessageId, jData); } } catch (Exception e) { diff --git a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java index f2f32fb48df..d21f090d4ad 100644 --- a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -1,6 +1,7 @@ package io.openems.common.websocket; import java.util.Optional; +import java.util.Map.Entry; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -8,10 +9,14 @@ import org.java_websocket.WebSocket; import com.google.common.collect.HashMultimap; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.Log; public abstract class CurrentDataWorker { @@ -25,34 +30,64 @@ public abstract class CurrentDataWorker { /** * Holds thingId and channelId, subscribed by this websocket */ - private final HashMultimap channels; + private final HashMultimap channels = HashMultimap.create(); /** * Holds the scheduled task for currentData */ - private final ScheduledFuture future; + private Optional> futureOpt = Optional.empty(); private final WebSocket websocket; - public CurrentDataWorker(WebSocket websocket, JsonObject jMessageId, HashMultimap channels) { + public CurrentDataWorker(WebSocket websocket) { this.websocket = websocket; - this.channels = channels; - this.future = this.executor.scheduleWithFixedDelay(() -> { - /* - * This task is executed regularly. Sends data to websocket. - */ - if (!this.websocket.isOpen()) { - // disconnected; stop worker - this.dispose(); - return; + } + + public synchronized void setChannels(JsonObject jSubscribeChannels, JsonObject jMessageId) { + // stop current thread + if (this.futureOpt.isPresent()) { + this.futureOpt.get().cancel(true); + this.futureOpt = Optional.empty(); + } + + // clear existing channels + this.channels.clear(); + + // parse and add subscribed channels + for (Entry entry : jSubscribeChannels.entrySet()) { + String thing = entry.getKey(); + try { + JsonArray jChannels = JsonUtils.getAsJsonArray(entry.getValue()); + for (JsonElement jChannel : jChannels) { + String channel = JsonUtils.getAsString(jChannel); + channels.put(thing, channel); + } + } catch (OpenemsException e) { + Log.warn("Unable to add channel subscription: " + e.getMessage()); } - WebSocketUtils.sendOrLogError(this.websocket, DefaultMessages.currentData(jMessageId, getSubscribedData())); - }, 0, UPDATE_INTERVAL_IN_SECONDS, TimeUnit.SECONDS); + } + if (!channels.isEmpty()) { + // registered channels -> create new thread + this.futureOpt = Optional.of(this.executor.scheduleWithFixedDelay(() -> { + /* + * This task is executed regularly. Sends data to websocket. + */ + if (!this.websocket.isOpen()) { + // disconnected; stop worker + this.dispose(); + return; + } + WebSocketUtils.sendOrLogError(this.websocket, + DefaultMessages.currentData(jMessageId, getSubscribedData())); + }, 0, UPDATE_INTERVAL_IN_SECONDS, TimeUnit.SECONDS)); + } } public void dispose() { // unsubscribe regular task - future.cancel(true); + if (this.futureOpt != null) { + futureOpt.get().cancel(true); + } } /** From 3b6d404d8a114fb4dc9d3d35d8d0b0297d5425e2 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Fri, 2 Mar 2018 23:40:33 +0100 Subject: [PATCH 115/156] Cleaning --- .../dummy/device/MetadataDummyDevice.java | 3 + .../device/MetadataDummyDeviceModel.java | 2 + .../file/device/MetadataFileDevice.java | 4 + .../backend/restapi/RestApiApplication.java | 58 ------ .../backend/restapi/RestApiSingleton.java | 23 --- .../restapi/route/DevicesAllRestlet.java | 43 ---- .../websocket/EdgeWebsocketHandler.java | 193 +----------------- .../websocket/WebsocketLogAppender.java | 2 +- .../streetscooter/StreetscooterEss1.java | 1 + .../streetscooter/StreetscooterEss2.java | 1 + .../fenecon/FeneconPersistence.java | 27 +-- .../fenecon/ReconnectingWebsocket.java | 57 ++++-- .../internal/range/ModbusCoilRange.java | 75 +++---- .../BackendApp.bndrun | 8 +- .../backend/application/BackendApp.java | 42 +--- .../bnd.bnd | 4 +- .../impl/provider/EdgeWebsocket.java | 32 +-- .../impl/provider/EdgeWebsocketServer.java | 19 +- .../io/openems/backend/metadata/api/Edge.java | 17 +- .../openems/backend/metadata/odoo/Odoo.java | 13 +- .../backend/metadata/odoo/OdooUtils.java | 57 ++++++ .../backend/timedata/api/TimedataService.java | 2 +- .../backend/timedata/influx/Influx.java | 14 +- .../timedata/influx/InfluxdbUtils.java | 12 +- .../org.eclipse.core.resources.prefs | 2 +- .../bnd.bnd | 4 +- ....backend.uiwebsocket.impl.provider.bndrun} | 0 .../impl/provider/UiWebsocket.java | 33 +-- .../impl/provider/UiWebsocketServer.java | 51 +++-- .../src/io/openems/common/config/Config.java | 2 - .../common/config/JsonPersistenceManager.java | 4 - .../io/openems/common/utils/StringUtils.java | 8 +- .../common/websocket/CurrentDataWorker.java | 2 +- .../common/websocket/Notification.java | 34 +-- ui/src/app/shared/device/device.ts | 1 + ui/src/environments/environment.ts | 8 +- 36 files changed, 307 insertions(+), 551 deletions(-) delete mode 100644 backend/src/main/java/io/openems/backend/restapi/RestApiApplication.java delete mode 100644 backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java delete mode 100644 backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java rename io.openems.backend.uiwebsocket.impl.provider/{io.openems.backend.browserwebsocket.impl.provider.bndrun => io.openems.backend.uiwebsocket.impl.provider.bndrun} (100%) diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java index de7e22bd285..ce2d8008f36 100644 --- a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java +++ b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java @@ -3,10 +3,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +// TODO migrate to OSGi public class MetadataDummyDevice { + @SuppressWarnings("unused") private final Logger log = LoggerFactory.getLogger(MetadataDummyDevice.class); + @SuppressWarnings("unused") private final int id; private final String apikey; diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java index 806cb1adae7..34506417287 100644 --- a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java +++ b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; +//TODO migrate to OSGi public class MetadataDummyDeviceModel { private static int lastId = 0; @@ -27,6 +28,7 @@ public MetadataDummyDeviceModel() {} // return result; // } + @SuppressWarnings("unused") private MetadataDummyDevice addNewDevice(String apikey) { int id = MetadataDummyDeviceModel.lastId++; MetadataDummyDevice device = new MetadataDummyDevice("openems" + id, "OpenEMS " + id, "Dummy Product", "admin", diff --git a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java index 2b0d37838d2..ae02680330a 100644 --- a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java +++ b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java @@ -3,11 +3,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +//TODO migrate to OSGi public class MetadataFileDevice { + @SuppressWarnings("unused") private final Logger log = LoggerFactory.getLogger(MetadataFileDevice.class); + @SuppressWarnings("unused") private final int id; + @SuppressWarnings("unused") private final String apikey; public MetadataFileDevice(String name, String comment, String producttype, String role, int id, String apikey) { diff --git a/backend/src/main/java/io/openems/backend/restapi/RestApiApplication.java b/backend/src/main/java/io/openems/backend/restapi/RestApiApplication.java deleted file mode 100644 index b0b07f3a2ad..00000000000 --- a/backend/src/main/java/io/openems/backend/restapi/RestApiApplication.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.backend.restapi; - -import org.restlet.Application; -import org.restlet.Restlet; -import org.restlet.routing.Router; - -import io.openems.backend.restapi.route.DevicesAllRestlet; - -public class RestApiApplication extends Application { - - /** - * Creates a root Restlet that will receive all incoming calls. - */ - @Override - public synchronized Restlet createInboundRoot() { - // ChallengeAuthenticator guard = createAuthenticator(); - Router router = createRouter(); - // guard.setNext(router); - // return guard; - return router; - } - - // TODO authentication - // private ChallengeAuthenticator createAuthenticator() { - // ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(), ChallengeScheme.HTTP_BASIC, - // "OpenEMS REST-Api"); - // guard.setVerifier(new OpenemsVerifier()); - // guard.setEnroler(new OpenemsEnroler()); - // return guard; - // } - - private Router createRouter() { - Router router = new Router(getContext()); - router.attach("/devices/all", new DevicesAllRestlet()); - // router.attach("/device/{deviceId}", new ChannelRestlet()); - return router; - } -} diff --git a/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java b/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java deleted file mode 100644 index bc1715fe129..00000000000 --- a/backend/src/main/java/io/openems/backend/restapi/RestApiSingleton.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.openems.backend.restapi; - -import org.restlet.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RestApiSingleton { - - private final Logger log = LoggerFactory.getLogger(RestApiSingleton.class); - private final Component component; - - public RestApiSingleton(int port) { - this.component = new Component(); - // this.component.getServers().add(Protocol.HTTP, port); - // this.component.getDefaultHost().attach("/rest", new RestApiApplication()); - // try { - // this.component.start(); - // } catch (Exception e) { - // throw new OpenemsException("Starting REST-Api failed: " + e.getMessage()); - // } - // log.info("REST-Api started on port [" + port + "]."); - } -} diff --git a/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java b/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java deleted file mode 100644 index ff4313e863f..00000000000 --- a/backend/src/main/java/io/openems/backend/restapi/route/DevicesAllRestlet.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.backend.restapi.route; - -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; - -public class DevicesAllRestlet extends Restlet { - - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - - // call handler methods - // if (request.getMethod().equals(Method.GET)) { - // JsonArray j = new JsonArray(); - // for (OpenemsSession session : OpenemsWebsocket.instance().getSessions()) { - // j.add(session.getData().getDevices().toJson()); - // } - // Representation entity = new StringRepresentation(j.toString(), MediaType.APPLICATION_JSON); - // response.setEntity(entity); - // } - } -} diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java index f7d8829dfc5..d720f12d1fa 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java @@ -91,13 +91,12 @@ public class EdgeWebsocketHandler { */ private Optional apiWorkerOpt = Optional.empty(); - @Deprecated - public EdgeWebsocketHandler() { - this.websocket = null; + public EdgeWebsocketHandler(WebSocket websocket) { + this.websocket = websocket; } public EdgeWebsocketHandler(WebSocket websocket, ApiWorker apiWorker) { - this.websocket = websocket; + this(websocket); this.apiWorkerOpt = Optional.ofNullable(apiWorker); } @@ -149,20 +148,6 @@ public final void onMessage(JsonObject jMessage) { // get MessageId from message -> used for reply Optional jMessageIdOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "messageId"); - // get role - // TODO - // Role role; - // if (this.userOpt.isPresent()) { - // role = this.userOpt.get().getRole(); - // } else { - // Optional roleStringOpt = JsonUtils.getAsOptionalString(jMessage, "role"); - // if (roleStringOpt.isPresent()) { - // role = Role.getRole(roleStringOpt.get()); - // } else { - // role = Role.getDefaultRole(); - // } - // } - if (jMessageIdOpt.isPresent()) { JsonObject jMessageId = jMessageIdOpt.get(); /* @@ -179,7 +164,7 @@ public final void onMessage(JsonObject jMessage) { */ Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); if (jCurrentDataOpt.isPresent()) { - currentData(jMessageId, jCurrentDataOpt.get()); + this.currentData(jMessageId, jCurrentDataOpt.get()); return; } @@ -188,7 +173,7 @@ public final void onMessage(JsonObject jMessage) { */ Optional jhistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); if (jhistoricDataOpt.isPresent()) { - historicData(jMessageId, jhistoricDataOpt.get()); + this.historicData(jMessageId, jhistoricDataOpt.get()); return; } @@ -198,7 +183,7 @@ public final void onMessage(JsonObject jMessage) { Optional jLogOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "log"); if (jLogOpt.isPresent()) { try { - log(role, jMessageId, jLogOpt.get()); + this.log(role, jMessageId, jLogOpt.get()); } catch (OpenemsException e) { log.error(e.getMessage()); } @@ -210,7 +195,7 @@ public final void onMessage(JsonObject jMessage) { Optional jSystemOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "system"); if (jSystemOpt.isPresent()) { try { - system(jMessageId, jSystemOpt.get(), role); + this.system(jMessageId, jSystemOpt.get(), role); } catch (OpenemsException e) { // TODO create notification log.error(e.getMessage()); @@ -418,51 +403,6 @@ private synchronized void system(JsonObject jMessageId, JsonObject jSystem, Role WebSocketUtils.send(this.websocket, DefaultMessages.systemExecuteReply(jMessageId, output)); } - // TODO handle config command - // /** - // * Set configuration - // * - // * @param j - // */ - // private synchronized void configure(JsonElement jConfigsElement) { - // try { - // JsonArray jConfigs = JsonUtils.getAsJsonArray(jConfigsElement); - // ThingRepository thingRepository = ThingRepository.getInstance(); - // for (JsonElement jConfigElement : jConfigs) { - // JsonObject jConfig = JsonUtils.getAsJsonObject(jConfigElement); - // String mode = JsonUtils.getAsString(jConfig, "mode"); - // if (mode.equals("update")) { - // /* - // * Channel Set mode - // */ - // String thingId = JsonUtils.getAsString(jConfig, "thing"); - // String channelId = JsonUtils.getAsString(jConfig, "channel"); - // JsonElement jValue = JsonUtils.getSubElement(jConfig, "value"); - // Optional channelOptional = thingRepository.getChannel(thingId, channelId); - // if (channelOptional.isPresent()) { - // Channel channel = channelOptional.get(); - // if (channel instanceof ConfigChannel) { - // /* - // * ConfigChannel - // */ - // ConfigChannel configChannel = (ConfigChannel) channel; - // configChannel.updateValue(jValue, true); - // Notification.send(NotificationType.SUCCESS, - // "Successfully updated [" + channel.address() + "] to [" + jValue + "]"); - // - // } else if (channel instanceof WriteChannel) { - // /* - // * WriteChannel - // */ - // WriteChannel writeChannel = (WriteChannel) channel; - // writeChannel.pushWrite(jValue); - // Notification.send(NotificationType.SUCCESS, - // "Successfully set [" + channel.address() + "] to [" + jValue + "]"); - // } - // } else { - // throw new ConfigException("Unable to find " + jConfig.toString()); - // } - // } else if (mode.equals("create")) { // /* // * Create new Thing // */ @@ -512,125 +452,6 @@ private synchronized void system(JsonObject jMessageId, JsonObject jSystem, Role // } // } - // TODO handle system command - // /** - // * System command - // * - // * @param j - // */ - // private synchronized void system(JsonElement jSystemElement) { - // JsonObject jNotification = new JsonObject(); - // try { - // JsonObject jSystem = JsonUtils.getAsJsonObject(jSystemElement); - // String mode = JsonUtils.getAsString(jSystem, "mode"); - // if (mode.equals("systemd-restart")) { - // /* - // * Restart systemd service - // */ - // String service = JsonUtils.getAsString(jSystem, "service"); - // if (service.equals("fems-pagekite")) { - // ProcessBuilder builder = new ProcessBuilder("/bin/systemctl", "restart", "fems-pagekite"); - // Process p = builder.start(); - // if (p.waitFor() == 0) { - // log.info("Successfully restarted fems-pagekite"); - // } else { - // throw new OpenemsException("restart fems-pagekite failed"); - // } - // } else { - // throw new OpenemsException("Unknown systemd-restart service: " + jSystemElement.toString()); - // } - // - // } else if (mode.equals("manualpq")) { - // /* - // * Manual PQ settings - // */ - // String ess = JsonUtils.getAsString(jSystem, "ess"); - // Boolean active = JsonUtils.getAsBoolean(jSystem, "active"); - // if (active) { - // Long p = JsonUtils.getAsLong(jSystem, "p"); - // Long q = JsonUtils.getAsLong(jSystem, "q"); - // if (this.controller == null) { - // throw new OpenemsException("Local access only. Controller is null."); - // } - // this.controller.setManualPQ(ess, p, q); - // Notification.send(NotificationType.SUCCESS, - // "Leistungsvorgabe gesetzt: ess[" + ess + "], p[" + p + "], q[" + q + "]"); - // } else { - // this.controller.resetManualPQ(ess); - // Notification.send(NotificationType.SUCCESS, "Leistungsvorgabe gestoppt: ess[" + ess + "]"); - // } - // } else { - // throw new OpenemsException("Unknown system message: " + jSystemElement.toString()); - // } - // } catch (OpenemsException | IOException | InterruptedException e) { - // Notification.send(NotificationType.ERROR, e.getMessage()); - // } - // } - - // TODO handle manual PQ - // private void manualPQ(JsonElement j, AuthenticatedWebsocketHandler handler) { - // try { - // JsonObject jPQ = JsonUtils.getAsJsonObject(j); - // if (jPQ.has("p") && jPQ.has("q")) { - // long p = JsonUtils.getAsLong(jPQ, "p"); - // long q = JsonUtils.getAsLong(jPQ, "q"); - // this.controller.setManualPQ(p, q); - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabe gesetzt: P=" + p + ",Q=" + q); - // } else { - // // stop manual PQ - // this.controller.resetManualPQ(); - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabe zurückgesetzt"); - // } - // } catch (ReflectionException e) { - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabewerte falsch: " + e.getMessage()); - // } - // } - - // TODO handle channel commands - // private void channel(JsonElement jChannelElement, AuthenticatedWebsocketHandler handler) { - // try { - // JsonObject jChannel = JsonUtils.getAsJsonObject(jChannelElement); - // String thingId = JsonUtils.getAsString(jChannel, "thing"); - // String channelId = JsonUtils.getAsString(jChannel, "channel"); - // JsonElement jValue = JsonUtils.getSubElement(jChannel, "value"); - // - // // get channel - // Channel channel; - // Optional channelOptional = thingRepository.getChannel(thingId, channelId); - // if (channelOptional.isPresent()) { - // // get channel value - // channel = channelOptional.get(); - // } else { - // // Channel not found - // throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); - // } - // - // // check for writable channel - // if (!(channel instanceof WriteChannel)) { - // throw new ResourceException(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - // } - // - // // set channel value - // if (channel instanceof ConfigChannel) { - // // is a ConfigChannel - // ConfigChannel configChannel = (ConfigChannel) channel; - // try { - // configChannel.updateValue(jValue, true); - // log.info("Updated Channel [" + channel.address() + "] to value [" + jValue.toString() + "]."); - // handler.sendNotification(NotificationType.SUCCESS, - // "Channel [" + channel.address() + "] aktualisiert zu [" + jValue.toString() + "]."); - // } catch (NotImplementedException e) { - // throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Conversion not implemented"); - // } - // } else { - // // is a WriteChannel - // handler.sendNotification(NotificationType.WARNING, "WriteChannel nicht implementiert"); - // } - // } catch (ReflectionException e) { - // handler.sendNotification(NotificationType.SUCCESS, "Leistungsvorgabewerte falsch: " + e.getMessage()); - // } - // } - /** * Send a message to the websocket. * diff --git a/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java b/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java index fd5d4de3f80..695f25e6ab4 100644 --- a/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java +++ b/edge/src/io/openems/core/utilities/websocket/WebsocketLogAppender.java @@ -31,7 +31,7 @@ protected void append(ILoggingEvent event) { ThingRepository.getInstance().getPersistences().forEach((persistence) -> { if (persistence instanceof FeneconPersistence) { FeneconPersistence p = (FeneconPersistence) persistence; - p.getWebsocketHandler().sendLog(timestamp, level, source, message); + p.sendLog(timestamp, level, source, message); } }); } diff --git a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java index 3e701e3d1c8..1b3d04ee38f 100644 --- a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java +++ b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java @@ -178,6 +178,7 @@ public ReadChannel allowedApparent() { /* * Methods */ + @SuppressWarnings("unchecked") @Override protected ModbusProtocol defineModbusProtocol() throws ConfigException { ModbusProtocol protokol = new ModbusProtocol(new ModbusRegisterRange(30001, // diff --git a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java index 65ef5d690aa..70132ed2515 100644 --- a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java +++ b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java @@ -178,6 +178,7 @@ public ReadChannel allowedApparent() { /* * Methods */ + @SuppressWarnings("unchecked") @Override protected ModbusProtocol defineModbusProtocol() throws ConfigException { ModbusProtocol protokol = new ModbusProtocol(new ModbusRegisterRange(31001, // diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index db5c5eec949..04e525d801b 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -55,13 +55,13 @@ import io.openems.common.types.NullFieldValue; import io.openems.common.types.NumberFieldValue; import io.openems.common.types.StringFieldValue; +import io.openems.common.utils.StringUtils; import io.openems.common.websocket.DefaultMessages; import io.openems.common.websocket.WebSocketUtils; import io.openems.core.Config; import io.openems.core.ConfigFormat; import io.openems.core.Databus; import io.openems.core.ThingRepository; -import io.openems.core.utilities.websocket.EdgeWebsocketHandler; // TODO make sure this is registered as ChannelChangeListener also to ConfigChannels @ThingInfo(title = "FENECON Persistence", description = "Establishes the connection to FENECON Cloud.") @@ -98,9 +98,8 @@ public class FeneconPersistence extends Persistence implements ChannelChangeList * Constructor */ public FeneconPersistence() { - this.websocketHandler = new EdgeWebsocketHandler(); this.thingState = new ThingStateChannels(this); - this.reconnectingWebsocket = new ReconnectingWebsocket(this.websocketHandler, (websocket) -> { + this.reconnectingWebsocket = new ReconnectingWebsocket((websocket) -> { /* * onOpen */ @@ -138,7 +137,6 @@ public void init() { * Fields */ private static final int DEFAULT_CYCLETIME = 10000; - private final EdgeWebsocketHandler websocketHandler; private final ReconnectingWebsocket reconnectingWebsocket; // Queue of data for the next cycle @@ -214,7 +212,7 @@ protected void forever() { } // Send data to Server - if (this.send(j)) { + if (this.sendOrLogError(j)) { // Successful // reset cycleTime @@ -223,7 +221,7 @@ protected void forever() { // resend from cache for (Iterator iterator = unsentCache.iterator(); iterator.hasNext();) { JsonObject jCached = iterator.next(); - boolean cacheWasSent = this.send(jCached); + boolean cacheWasSent = this.sendOrLogError(jCached); if (cacheWasSent) { iterator.remove(); } @@ -249,13 +247,14 @@ protected void dispose() { * * @param j * @return + * @throws OpenemsException */ - private boolean send(JsonObject j) { + private boolean sendOrLogError(JsonObject j) { try { - this.websocketHandler.send(j); + this.reconnectingWebsocket.send(j); return true; } catch (OpenemsException e) { - log.error(e.getMessage()); + log.warn("Unable to send: " + StringUtils.toShortString(j, 100)); return false; } } @@ -265,9 +264,9 @@ private boolean send(JsonObject j) { * * @return */ - public EdgeWebsocketHandler getWebsocketHandler() { - return this.websocketHandler; - } + // public EdgeWebsocketHandler getWebsocketHandler() { + // return this.websocketHandler; + // } private void increaseCycleTime() { int currentCycleTime = this.getCycleTime(); @@ -391,4 +390,8 @@ private Optional proxyInfo() { public ThingStateChannels getStateChannel() { return this.thingState; } + + public void sendLog(long timestamp, String level, String source, String message) { + this.reconnectingWebsocket.sendLog(timestamp, level, source, message); + } } \ No newline at end of file diff --git a/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java b/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java index 952f1ee8706..b332afc0dd3 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java +++ b/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java @@ -25,6 +25,9 @@ import com.google.gson.JsonParser; import io.openems.App; +import io.openems.api.security.User; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.websocket.WebSocketUtils; import io.openems.core.utilities.Mutex; import io.openems.core.utilities.websocket.EdgeWebsocketHandler; @@ -35,14 +38,13 @@ public class ReconnectingWebsocket { private final int MAX_WAIT_AFTER_CLOSE = 60 * 3; // 3 minutes private int WAIT_AFTER_CLOSE = DEFAULT_WAIT_AFTER_CLOSE; private final Draft WEBSOCKET_DRAFT = new Draft_6455(); - private final EdgeWebsocketHandler WEBSOCKET_HANDLER; private final Mutex WEBSOCKET_CLOSED = new Mutex(true); private Optional uriOpt = Optional.empty(); private Optional proxyOpt = Optional.empty(); private final Map httpHeaders = new HashMap<>(); private final OnOpenListener ON_OPEN_LISTENER; private final OnCloseListener ON_CLOSE_LISTENER; - private Optional WEBSOCKET_OPT = Optional.empty(); + private Optional WEBSOCKET_OPT = Optional.empty(); private final ScheduledExecutorService reconnectorExecutor = Executors .newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("Re-Ws-%d").build()); @@ -57,6 +59,8 @@ public class ReconnectingWebsocket { * @author stefan.feilmeier */ private class MyWebSocketClient extends WebSocketClient { + private final EdgeWebsocketHandler handler ; + public MyWebSocketClient(URI uri, Map httpHeaders, Proxy proxy) throws IOException { this(uri, httpHeaders); this.setProxy(proxy); @@ -64,8 +68,9 @@ public MyWebSocketClient(URI uri, Map httpHeaders, Proxy proxy) public MyWebSocketClient(URI uri, Map httpHeaders) throws IOException { super(uri, WEBSOCKET_DRAFT, httpHeaders, 0); - log.info("I was built. ID [" + Thread.currentThread().getId() + "] name [" - + Thread.currentThread().getName() + "]"); + EdgeWebsocketHandler handler = new EdgeWebsocketHandler(this); + handler.setUser(User.ADMIN); + this.handler = handler; } @Override @@ -79,7 +84,7 @@ public void onOpen(ServerHandshake handshakedata) { public void onMessage(String message) { try { JsonObject jMessage = (new JsonParser()).parse(message).getAsJsonObject(); - WEBSOCKET_HANDLER.onMessage(jMessage); + this.handler.onMessage(jMessage); } catch (OutOfMemoryError e) { // Java-Websocket library can cause an "unable to create new native thread" OutOfMemoryError on // subscribe. We are not able to recover that. @@ -96,7 +101,7 @@ public void onMessage(String message) { public void onClose(int code, String reason, boolean remote) { log.info("Websocket [" + this.getURI().toString() + "] closed. Code [" + code + "] Reason [" + reason + "] Wait [" + WAIT_AFTER_CLOSE + "]"); - WAIT_AFTER_CLOSE += DEFAULT_WAIT_AFTER_CLOSE; + WAIT_AFTER_CLOSE += DEFAULT_WAIT_AFTER_CLOSE * 2; if (WAIT_AFTER_CLOSE > MAX_WAIT_AFTER_CLOSE) { WAIT_AFTER_CLOSE = MAX_WAIT_AFTER_CLOSE; } @@ -113,10 +118,13 @@ public void onError(Exception ex) { @Override protected void finalize() throws Throwable { - System.out.println("Finalize... [" + Thread.currentThread().getId() + "] name [" - + Thread.currentThread().getName() + "]"); + this.handler.dispose(); super.finalize(); } + + protected void sendLog(long timestamp, String level, String source, String message) { + this.handler.sendLog(timestamp, level, source, message); + } } @FunctionalInterface @@ -135,9 +143,7 @@ public interface OnCloseListener { * @param uri * @param httpHeaders */ - public ReconnectingWebsocket(EdgeWebsocketHandler handler, OnOpenListener onOpenListener, - OnCloseListener onCloseListener) { - this.WEBSOCKET_HANDLER = handler; + public ReconnectingWebsocket(OnOpenListener onOpenListener, OnCloseListener onCloseListener) { this.ON_OPEN_LISTENER = onOpenListener; this.ON_CLOSE_LISTENER = onCloseListener; this.reconnectorTask = () -> { @@ -164,20 +170,18 @@ public ReconnectingWebsocket(EdgeWebsocketHandler handler, OnOpenListener onOpen } // Create new websocket and open connection - WebSocketClient ws; - if(this.proxyOpt.isPresent()) { + MyWebSocketClient ws; + if (this.proxyOpt.isPresent()) { ws = new MyWebSocketClient(uriOpt.get(), httpHeaders, this.proxyOpt.get()); } else { ws = new MyWebSocketClient(uriOpt.get(), httpHeaders); } ws.connect(); WEBSOCKET_OPT = Optional.of(ws); - // TODO: websocket cannot be changed - // WEBSOCKET_HANDLER.setWebsocket(ws); } catch (Throwable t) { String wsString = uriOpt.isPresent() ? uriOpt.get().toString() : "NO_URI"; - log.error("Websocket [" + wsString + " reconnect error: " + t.getMessage()); + log.error("Websocket [" + wsString + "] reconnect error. " + t.getClass().getSimpleName() + ": " + t.getMessage()); } } }; @@ -222,4 +226,25 @@ public boolean websocketIsOpen() { } return false; } + + /** + * Send message to websocket + * + * @param j + * @return + * @throws OpenemsException + */ + protected void send(JsonObject j) throws OpenemsException { + if (WEBSOCKET_OPT.isPresent()) { + WebSocketUtils.send(WEBSOCKET_OPT.get(), j); + } else { + throw new OpenemsException("No Websocket!"); + } + } + + public void sendLog(long timestamp, String level, String source, String message) { + if(this.WEBSOCKET_OPT.isPresent()) { + this.WEBSOCKET_OPT.get().sendLog(timestamp, level, source, message); + } + } } diff --git a/edge/src/io/openems/impl/protocol/modbus/internal/range/ModbusCoilRange.java b/edge/src/io/openems/impl/protocol/modbus/internal/range/ModbusCoilRange.java index 6bbc1015e20..0ef79f01392 100644 --- a/edge/src/io/openems/impl/protocol/modbus/internal/range/ModbusCoilRange.java +++ b/edge/src/io/openems/impl/protocol/modbus/internal/range/ModbusCoilRange.java @@ -1,37 +1,38 @@ -/******************************************************************************* - * 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.protocol.modbus.internal.range; - -import io.openems.impl.protocol.modbus.ModbusElement; - -public class ModbusCoilRange extends ModbusRange { - - public ModbusCoilRange(int startAddress, ModbusElement... elements) { - super(startAddress, elements); - } - - @SuppressWarnings("unchecked") - @Override - public ModbusElement[] getElements() { - return (ModbusElement[]) super.getElements(); - } - -} +/******************************************************************************* + * 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.protocol.modbus.internal.range; + +import io.openems.impl.protocol.modbus.ModbusElement; + +public class ModbusCoilRange extends ModbusRange { + + @SuppressWarnings("unchecked") + public ModbusCoilRange(int startAddress, ModbusElement... elements) { + super(startAddress, elements); + } + + @SuppressWarnings("unchecked") + @Override + public ModbusElement[] getElements() { + return (ModbusElement[]) super.getElements(); + } + +} diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index b260e2053fe..fa2067c4210 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -10,11 +10,15 @@ JPM-Command: openems-backend -runrequires: \ osgi.identity;filter:='(osgi.identity=io.openems.backend.application)',\ osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-service)',\ - osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-api)' + osgi.identity;filter:='(osgi.identity=org.ops4j.pax.logging.pax-logging-api)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.edgewebsocket.impl.provider)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.odoo.provider)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx.provider)',\ + osgi.identity;filter:='(osgi.identity=io.openems.backend.uiwebsocket.impl.provider)' -runproperties: \ configFile=C:/openems-config/backend.json,\ org.ops4j.pax.logging.service.frameworkEventsLogLevel="DISABLED" -#-runproperties: felix.cm.dir=/etc/openems.d + -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' -runee: JavaSE-1.8 diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index 00435e575de..bf5bc7d4f1c 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -9,15 +9,9 @@ import org.osgi.service.component.annotations.Component; 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.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; -import io.openems.backend.metadata.api.MetadataService; -import io.openems.backend.uiwebsocket.api.UiWebsocketService; - @Component() public class BackendApp { @@ -26,44 +20,10 @@ public class BackendApp { @Reference private ConfigurationAdmin configAdmin; - @Reference - private MetadataService metadataService; - - // @Reference - // TimedataService timedataService; - - @Reference - private volatile EdgeWebsocketService edgeWebsocketService; - - @Reference - private UiWebsocketService uiWebsocketService; - - // @Reference(target = "(component.factory=EdgeWebsocketFactory)") - // private ComponentFactory factory; - - // @Reference(cardinality = ReferenceCardinality.MULTIPLE, bind = "bind", unbind - // = "unbind", policy = ReferencePolicy.DYNAMIC) - // List list; - // - // protected void bind(OneShot filter) { - // if (list == null) { - // list = new ArrayList(); - // } - // list.add(filter); - // System.out.println("add " + filter); - // } - // - // protected void unbind(OneShot filter) { - // list.remove(filter); - // System.out.println("remove " + filter); - // } - @Activate void activate() { - configureLogging(); - // TODO implement MessageManager to decouple components log.debug("Activate BackendApp"); - + configureLogging(); } private void configureLogging() { diff --git a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd index 8452b58f346..37e1fc1e512 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.edgewebsocket.impl.provider/bnd.bnd @@ -17,9 +17,9 @@ Export-Package: io.openems.backend.edgewebsocket.api io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ io.openems.backend.common;version=latest,\ - io.openems.backend.uiwebsocket.api;version=latest,\ io.openems.backend.timedata.api;version=latest,\ - com.google.gson;version=2.8 + com.google.gson;version=2.8,\ + io.openems.backend.uiwebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java index 2cb498a9d16..a51d7ba26b6 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocket.java @@ -1,7 +1,5 @@ package io.openems.backend.edgewebsocket.impl.provider; -import java.io.IOException; - import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; @@ -10,6 +8,8 @@ import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.event.EventAdmin; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,24 +21,21 @@ import io.openems.backend.uiwebsocket.api.UiWebsocketService; import io.openems.common.exceptions.OpenemsException; -import org.osgi.service.metatype.annotations.Designate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; - @Designate(ocd = EdgeWebsocket.Config.class, factory = false) -@Component(name = "EdgeWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE) +@Component(name = "EdgeWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true) public class EdgeWebsocket implements EdgeWebsocketService { private final Logger log = LoggerFactory.getLogger(EdgeWebsocket.class); private EdgeWebsocketServer server = null; - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + @Reference protected volatile MetadataService metadataService; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) protected volatile UiWebsocketService uiWebsocketService; - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + @Reference protected volatile TimedataService timedataService; @Reference @@ -68,11 +65,22 @@ void deactivate() { */ private synchronized void stopServer() { if (this.server != null) { - try { - this.server.stop(); - } catch (NullPointerException | IOException | InterruptedException e) { - log.error("Unable to stop existing EdgeWebsocketServer: " + e.getMessage()); + int tries = 3; + while (tries-- > 0) { + try { + this.server.stop(1000); + return; + } catch (NullPointerException | InterruptedException e) { + log.warn("Unable to stop existing EdgeWebsocketServer. " + e.getClass().getSimpleName() + ": " + + e.getMessage()); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + /* ignore */ + } + } } + log.error("Stopping EdgeWebsocketServer failed too often."); } } diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 6fa5718ffe1..83a698a9a34 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -89,23 +89,6 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { log.info("Device [ID:" + edgeId + "] connected."); } } - - // TODO do this in Metadata - // try { - // // set device active (in Odoo) - // for (MetadataDevice device : devices) { - // if (device.getState().equals("inactive")) { - // device.setState("active"); - // } - // device.setLastMessage(); - // device.writeObject(); - // } - // } catch (OpenemsException e) { - // // this error does not stop the connection - // log.error("Device [" + String.join(",", deviceNames) + "] error: " + - // e.getMessage()); - // } - } catch (OpenemsException e) { // send connection failed to OpenEMS JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); @@ -228,7 +211,7 @@ private void timedata(int[] edgeIds, JsonObject jTimedata) { log.debug("Edge [" + edge.getName() + "] wrote " + jTimedata.entrySet().size() + " timestamps " + StringUtils.toShortString(jTimedata, 120)); } catch (Exception e) { - log.error("Unable to write Timedata: ", e); + log.error("Unable to write Timedata: " + e.getClass().getSimpleName() + ": " + e.getMessage()); } /* * set last update timestamps in MetadataService 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 e707e1f8908..468a6633067 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 @@ -39,7 +39,6 @@ public String getName() { */ public void setOnline(boolean isOnline) { this.isOnline = isOnline; - // TODO call OnSetOnline } private Optional onSetConfig = Optional.empty(); @@ -62,7 +61,7 @@ public JsonObject getConfig() { public String getProducttype() { return producttype; } - + public boolean isOnline() { return this.isOnline; } @@ -88,46 +87,46 @@ public String toString() { public void onSetLastMessage(OnSetZonedDateTime listener) { this.onSetLastMessage = Optional.of(listener); } - + public void setLastMessage() { this.lastMessage = ZonedDateTime.now(ZoneOffset.UTC); if (this.onSetLastMessage.isPresent()) { this.onSetLastMessage.get().call(this.lastMessage); } } - + private Optional onSetLastUpdate = Optional.empty(); public void onSetLastUpdate(OnSetZonedDateTime listener) { this.onSetLastUpdate = Optional.of(listener); } - + public void setLastUpdate() { this.lastUpdate = ZonedDateTime.now(ZoneOffset.UTC); if (this.onSetLastUpdate.isPresent()) { this.onSetLastUpdate.get().call(this.lastUpdate); } } - + private Optional onSetSoc = Optional.empty(); public void onSetSoc(OnSetInteger listener) { this.onSetSoc = Optional.of(listener); } - + public void setSoc(int soc) { this.soc = soc; if (this.onSetSoc.isPresent()) { this.onSetSoc.get().call(this.soc); } } - + private Optional onSetIpv4 = Optional.empty(); public void onSetIpv4(OnSetString listener) { this.onSetIpv4 = Optional.of(listener); } - + public void setIpv4(String ipv4) { this.ipv4 = ipv4; if (this.onSetIpv4.isPresent()) { diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java index 648d0f5a9a0..e93d71e15ab 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/Odoo.java @@ -55,7 +55,7 @@ public class Odoo implements MetadataService { String url() default "https://www1.fenecon.de"; } - + private String url; private String database; private int uid; @@ -118,13 +118,7 @@ public User getUserWithSession(String sessionId) throws OpenemsException { if (j.has("error")) { JsonObject jError = JsonUtils.getAsJsonObject(j, "error"); String errorMessage = JsonUtils.getAsString(jError, "message"); - switch (errorMessage) { - case "Odoo Session Expired": - // TODO handle exception - throw new OpenemsException("Odoo Session Expired"); - default: - throw new OpenemsException("Odoo replied with error: " + errorMessage); - } + throw new OpenemsException("Odoo replied with error: " + errorMessage); } if (j.has("result")) { @@ -152,10 +146,8 @@ public User getUserWithSession(String sessionId) throws OpenemsException { return user; } } - // TODO handle exception throw new OpenemsException("No matching user found"); } catch (IOException e) { - // TODO handle exception throw new OpenemsException("IOException while reading from Odoo: " + e.getMessage()); } finally { if (connection != null) { @@ -167,7 +159,6 @@ public User getUserWithSession(String sessionId) throws OpenemsException { @Override public int[] getEdgeIdsForApikey(String apikey) { try { - // TODO use Odoo "search_read" method int[] edgeIds = OdooUtils.search(this.url, this.database, this.uid, this.password, "fems.device", new Domain("apikey", "=", apikey)); // refresh Edge cache diff --git a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java index 0e53e25b25a..4ff038f9601 100644 --- a/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java +++ b/io.openems.backend.metadata.odoo.provider/src/io/openems/backend/metadata/odoo/OdooUtils.java @@ -186,6 +186,63 @@ protected static Map[] readMany(String url, String database, int } } + /** + * Search-Reads multiple records from Odoo + * + * @param url + * URL of Odoo instance + * @param database + * Database name + * @param uid + * UID of user (e.g. '1' for admin) + * @param password + * Password of user + * @param model + * Odoo model to query (e.g. 'res.partner') + * @param ids + * ids of model to read + * @param fields + * fields that should be read + * @return + * @throws OpenemsException + */ + protected static Map[] searchRead(String url, String database, int uid, String password, String model, + Field[] fields, Domain... domains) throws OpenemsException { + // Create request params + String action = "search_read"; + // Add domain filter + Object[] domain = new Object[domains.length]; + for (int i = 0; i < domains.length; i++) { + Domain filter = domains[i]; + domain[i] = new Object[] { filter.field, filter.operator, filter.value }; + } + Object[] paramsDomain = new Object[] { domain }; + // Add fields + String[] fieldStrings = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + fieldStrings[i] = fields[i].toString(); + } + Map paramsFields = new HashMap<>(); + paramsFields.put("fields", fieldStrings); + // Create request params + Object[] params = new Object[] { database, uid, password, model, action, paramsDomain, paramsFields }; + try { + // Execute XML request + Object[] resultObjs = (Object[]) executeKw(url, params); + // Parse results + @SuppressWarnings("unchecked") + Map[] results = (Map[]) new Map[resultObjs.length]; + for (int i = 0; i < resultObjs.length; i++) { + @SuppressWarnings("unchecked") + Map result = (Map) resultObjs[i]; + results[0] = result; + } + return results; + } catch (Throwable e) { + throw new OpenemsException("Unable to read from Odoo: " + e.getMessage()); + } + } + /** * Update a record in Odoo * diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java index ddc940c240b..2623dd68fe9 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java +++ b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/TimedataService.java @@ -97,7 +97,7 @@ public default JsonArray queryHistoricData(Optional edgeIdOpt, JsonObje // TODO check if role is allowed to read these channels // JsonObject kWh = JsonUtils.getAsJsonObject(jQuery, "kWh"); int days = Period.between(fromDate.toLocalDate(), toDate.toLocalDate()).getDays(); - // TODO: better calculation of sensible resolution + // TODO better calculation of sensible resolution int resolution = 10 * 60; // 10 Minutes if (days > 25) { resolution = 24 * 60 * 60; // 1 Day diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index fe51d566e0f..a672f174cc4 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -129,8 +129,9 @@ private void writeData(int influxId, TreeBasedTable data) } /** - * Writes data to old database for old Mini monitoring TODO remove after full - * migration + * Writes data to old database for old Mini monitoring + * + * TODO remove after full migration * * @param device * @param data @@ -197,10 +198,11 @@ private void writeDataToOldMiniMonitoring(Edge edge, int influxId, TreeBasedTabl } @Override - public JsonArray queryHistoricData(Optional edgeIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, JsonObject channels, - int resolution) throws OpenemsException { - Optional influxIdOpt = Optional.empty(); // if given, query only this id. If not given, do not apply filter - if(edgeIdOpt.isPresent()) { + public JsonArray queryHistoricData(Optional edgeIdOpt, ZonedDateTime fromDate, ZonedDateTime toDate, + JsonObject channels, int resolution) throws OpenemsException { + Optional influxIdOpt = Optional.empty(); // if given, query only this id. If not given, do not apply + // filter + if (edgeIdOpt.isPresent()) { Edge edge = this.metadataService.getEdge(edgeIdOpt.get()); influxIdOpt = Optional.of(InfluxdbUtils.parseNumberFromName(edge.getName())); } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java index a9d31e18c55..c6ec779c8d3 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/InfluxdbUtils.java @@ -380,10 +380,14 @@ private static QueryResult executeQuery(InfluxDB influxdb, String database, Stri private final static Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); public static Integer parseNumberFromName(String name) throws OpenemsException { - Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); - if (matcher.find()) { - String nameNumberString = matcher.group(1); - return Integer.parseInt(nameNumberString); + try { + Matcher matcher = NAME_NUMBER_PATTERN.matcher(name); + if (matcher.find()) { + String nameNumberString = matcher.group(1); + return Integer.parseInt(nameNumberString); + } + } catch (NullPointerException e) { + /* ignore */ } throw new OpenemsException("Unable to parse number from name [" + name + "]"); } diff --git a/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs index 183a6298baa..c916e333e2e 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.uiwebsocket.impl.provider/.settings/org.eclipse.core.resources.prefs @@ -3,5 +3,5 @@ encoding//src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java=UTF- encoding//test/io/openems/backend/uiwebsocket/impl/provider/ProviderImplTest.java=UTF-8 encoding/bnd.bnd=UTF-8 encoding/debug.bndrun=UTF-8 -encoding/io.openems.backend.browserwebsocket.impl.provider.bndrun=UTF-8 +encoding/io.openems.backend.uiwebsocket.impl.provider.bndrun=UTF-8 encoding/readme.md=UTF-8 diff --git a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd index 97c3d7a596c..d0e7a9edbb9 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd +++ b/io.openems.backend.uiwebsocket.impl.provider/bnd.bnd @@ -18,8 +18,8 @@ Private-Package: io.openems.backend.uiwebsocket.impl.provider io.openems.common;version=latest,\ io.openems.backend.metadata.api;version=latest,\ io.openems.backend.uiwebsocket.api;version=latest,\ - io.openems.backend.edgewebsocket.api;version=latest,\ - io.openems.backend.timedata.api;version=latest + io.openems.backend.timedata.api;version=latest,\ + io.openems.backend.edgewebsocket.api;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun b/io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.uiwebsocket.impl.provider.bndrun similarity index 100% rename from io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.browserwebsocket.impl.provider.bndrun rename to io.openems.backend.uiwebsocket.impl.provider/io.openems.backend.uiwebsocket.impl.provider.bndrun diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 8c4ef9598e8..7d5f8a6dd8b 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -1,13 +1,13 @@ package io.openems.backend.uiwebsocket.impl.provider; -import java.io.IOException; - 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.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,23 +19,21 @@ import io.openems.backend.timedata.api.TimedataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; -import org.osgi.service.metatype.annotations.Designate; - @Designate(ocd = UiWebsocket.Config.class, factory = false) -@Component(name = "UiWebsocket") +@Component(name = "UiWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true) public class UiWebsocket implements UiWebsocketService { private final Logger log = LoggerFactory.getLogger(UiWebsocket.class); private UiWebsocketServer server = null; - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + @Reference protected volatile MetadataService metadataService; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) protected volatile EdgeWebsocketService edgeWebsocketService; - - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + + @Reference protected volatile TimedataService timeDataService; @ObjectClassDefinition @@ -62,11 +60,22 @@ void deactivate() { */ private void stopServer() { if (this.server != null) { - try { - this.server.stop(); - } catch (IOException | InterruptedException e) { - log.error("Unable to stop existing UiWebsocketServer: " + e.getMessage()); + int tries = 3; + while (tries-- > 0) { + try { + this.server.stop(1000); + return; + } catch (NullPointerException | InterruptedException e) { + log.warn("Unable to stop existing UiWebsocketServer. " + e.getClass().getSimpleName() + ": " + + e.getMessage()); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + /* ignore */ + } + } } + log.error("Stopping UiWebsocketServer failed too often."); } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index befe2656fa3..9e20a61e0ca 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -1,9 +1,5 @@ package io.openems.backend.uiwebsocket.impl.provider; -import java.time.Period; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -44,7 +40,6 @@ public UiWebsocketServer(UiWebsocket parent, int port) { @Override protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { - String error = ""; User user; // login using session_id from the cookie @@ -57,9 +52,10 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { // TODO fix bug in Odoo that is not reliably returning all configured devices } catch (OpenemsException e) { // send connection failed to browser - this.send(websocket, DefaultMessages.uiConnectionFailedReply()); - log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + error + "]."); - websocket.closeConnection(CloseFrame.REFUSE, error); + WebSocketUtils.sendOrLogError(websocket, DefaultMessages.uiConnectionFailedReply()); + log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + e.getMessage() + + "]."); + websocket.closeConnection(CloseFrame.REFUSE, e.getMessage()); return; } @@ -122,7 +118,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { int userId = data.getUserId(); Optional userOpt = this.parent.metadataService.getUser(userId); if (!userOpt.isPresent()) { - // TODO Error user not found + WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject(), LogBehaviour.WRITE_TO_LOG, + Notification.BACKEND_UNABLE_TO_READ_USER_DETAILS, userId); return; } User user = userOpt.get(); @@ -137,21 +134,22 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { JsonObject jMessageId = jMessageIdOpt.get(); int edgeId = edgeIdOpt.get(); - /* - * verify that User is allowed to access Edge - */ - if (!user.getEdgeRole(edgeId).isPresent()) { - // TODO Error Access denied - return; - } - // get Edge Edge edge; try { edge = this.parent.metadataService.getEdge(edgeId); } catch (OpenemsException e) { - // TODO handle error - log.error(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.BACKEND_UNABLE_TO_READ_EDGE_DETAILS, edgeId, e.getMessage()); + return; + } + + /* + * verify that User is allowed to access Edge + */ + if (!user.getEdgeRole(edgeId).isPresent()) { + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.BACKEND_FORWARD_TO_EDGE_NOT_ALLOWED, edge.getName(), user.getName()); return; } @@ -161,8 +159,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { Optional jHistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); if (jHistoricDataOpt.isPresent()) { JsonObject jHistoricData = jHistoricDataOpt.get(); - JsonObject jReply = this.historicData(jMessageId, edgeId, jHistoricData); - WebSocketUtils.sendOrLogError(websocket, jReply); + this.historicData(websocket, jMessageId, edgeId, jHistoricData); return; } @@ -196,7 +193,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { } /* - * TODO Forward to OpenEMS Edge + * Forward to OpenEMS Edge */ if (jMessage.has("config") || jMessage.has("log") || jMessage.has("system")) { try { @@ -252,7 +249,7 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, J * * @param j */ - private JsonObject historicData(JsonObject jMessageId, int edgeId, JsonObject jHistoricData) { + private void historicData(WebSocket websocket, JsonObject jMessageId, int edgeId, JsonObject jHistoricData) { try { String mode = JsonUtils.getAsString(jHistoricData, "mode"); @@ -261,13 +258,13 @@ private JsonObject historicData(JsonObject jMessageId, int edgeId, JsonObject jH * Query historic data */ JsonArray jData = this.parent.timeDataService.queryHistoricData(edgeId, jHistoricData); - return DefaultMessages.historicDataQueryReply(jMessageId, jData); + WebSocketUtils.sendOrLogError(websocket, DefaultMessages.historicDataQueryReply(jMessageId, jData)); + return; } } catch (Exception e) { - // TODO handle exception - log.warn(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.UNABLE_TO_QUERY_HISTORIC_DATA, edgeId, e.getMessage()); } - return new JsonObject(); } private String getUserName(WebsocketData data) { diff --git a/io.openems.common/src/io/openems/common/config/Config.java b/io.openems.common/src/io/openems/common/config/Config.java index 5fa17bc95b6..65772eb4790 100644 --- a/io.openems.common/src/io/openems/common/config/Config.java +++ b/io.openems.common/src/io/openems/common/config/Config.java @@ -22,8 +22,6 @@ public String getPid() { @Override public synchronized Object get(Object key) { Object o = super.get(key); - // log.debug("Reading from Config PID [" + this.getPid() + "]: [" + key + "=" + - // o + "]"); return o; } } diff --git a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java index 99430e80bec..bdcaed0baa2 100644 --- a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java +++ b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java @@ -46,13 +46,11 @@ void activate() { @Deactivate void deactivate() { - log.info("deactivate config"); this.configs.clear(); } @Override public void delete(String pid) throws IOException { - log.debug("Delete configuration for PID [" + pid + "]"); synchronized (this.configs) { if (this.configs.remove(pid) != null) { this.saveConfigMapToFile(); @@ -82,8 +80,6 @@ public Dictionary load(String pid) throws IOException { @Override public void store(String pid, @SuppressWarnings("rawtypes") Dictionary values) throws IOException { - log.debug("Store to Config. PID [" + pid + "]: " + values); - boolean configNeedsToBeAdded = false; boolean configChanged = false; synchronized (this.configs) { diff --git a/io.openems.common/src/io/openems/common/utils/StringUtils.java b/io.openems.common/src/io/openems/common/utils/StringUtils.java index 3522b85cf04..a52f8f3465f 100644 --- a/io.openems.common/src/io/openems/common/utils/StringUtils.java +++ b/io.openems.common/src/io/openems/common/utils/StringUtils.java @@ -4,8 +4,7 @@ public class StringUtils { - public static String toShortString(JsonObject j, int length) { - String s = j.toString(); + public static String toShortString(String s, int length) { if (s.length() > length - 3) { return s.substring(0, length - 3) + "..."; } else { @@ -13,6 +12,11 @@ public static String toShortString(JsonObject j, int length) { } } + public static String toShortString(JsonObject j, int length) { + String s = j.toString(); + return toShortString(s, length); + } + public static String capitalizeFirstLetter(String s) { return s.substring(0, 1).toUpperCase() + s.substring(1); } diff --git a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java index d21f090d4ad..93261203d7e 100644 --- a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -85,7 +85,7 @@ public synchronized void setChannels(JsonObject jSubscribeChannels, JsonObject j public void dispose() { // unsubscribe regular task - if (this.futureOpt != null) { + if (this.futureOpt.isPresent()) { futureOpt.get().cancel(true); } } diff --git a/io.openems.common/src/io/openems/common/websocket/Notification.java b/io.openems.common/src/io/openems/common/websocket/Notification.java index fb3f72b4270..1f2df2767a1 100644 --- a/io.openems.common/src/io/openems/common/websocket/Notification.java +++ b/io.openems.common/src/io/openems/common/websocket/Notification.java @@ -3,37 +3,43 @@ import org.slf4j.Logger; public enum Notification { - EDGE_CONNECTION_ClOSED(100, NotificationType.WARNING, "Connection [%s] was interrupted"), - EDGE_CONNECTION_OPENED(101, NotificationType.INFO, "Connection [%s] was established"), - EDGE_UNABLE_TO_FORWARD(102, NotificationType.ERROR, "Unable to forward command to [%s]: %s"), - EDGE_AUTHENTICATION_BY_TOKEN_FAILED(103, NotificationType.INFO, "Authentication by token [%s] failed"), - EDGE_CHANNEL_UPDATE_SUCCESS(104, NotificationType.SUCCESS, "Configuration successfully updated [%s]"), - EDGE_CHANNEL_UPDATE_FAILED(105, NotificationType.ERROR, "Configuration update failed [%s]: %s"), - BACKEND_NOT_ALLOWED(106, NotificationType.ERROR, "The operation [%s] is not allowed via Backend"), - EDGE_CHANNEL_UPDATE_TIMEOUT(107, NotificationType.INFO, "Channel setting [%s] timed out."); - + EDGE_CONNECTION_ClOSED(100, NotificationType.WARNING, "Connection [%s] was interrupted"), // + EDGE_CONNECTION_OPENED(101, NotificationType.INFO, "Connection [%s] was established"), // + EDGE_UNABLE_TO_FORWARD(102, NotificationType.ERROR, "Unable to forward command to [%s]: %s"), // + EDGE_AUTHENTICATION_BY_TOKEN_FAILED(103, NotificationType.INFO, "Authentication by token [%s] failed"), // + EDGE_CHANNEL_UPDATE_SUCCESS(104, NotificationType.SUCCESS, "Configuration successfully updated [%s]"), // + EDGE_CHANNEL_UPDATE_FAILED(105, NotificationType.ERROR, "Configuration update failed [%s]: %s"), // + BACKEND_NOT_ALLOWED(106, NotificationType.ERROR, "The operation [%s] is not allowed via Backend"), // + EDGE_CHANNEL_UPDATE_TIMEOUT(107, NotificationType.INFO, "Channel setting [%s] timed out."), // + BACKEND_FORWARD_TO_EDGE_NOT_ALLOWED(108, NotificationType.ERROR, + "Message forward to Edge [%s] is not allowed for user [%s]."), // + BACKEND_UNABLE_TO_READ_EDGE_DETAILS(109, NotificationType.ERROR, "Unable to read details for Edge [ID:%s]: %s"), // + UNABLE_TO_QUERY_HISTORIC_DATA(110, NotificationType.ERROR, "Unable to query historic data for Edge [ID:%s]: %s"), // + BACKEND_UNABLE_TO_READ_USER_DETAILS(111, NotificationType.ERROR, "Unable to read details for User [ID:%s]"); // + + private final int value; private final NotificationType status; private final String message; - + private Notification(int value, NotificationType status, String message) { this.value = value; this.status = status; this.message = message; } - + public int getValue() { return value; } - + public NotificationType getType() { return status; } - + public String getMessage() { return message; } - + public void writeToLog(Logger log, Object... params) { String message = String.format(this.message, params); String logMessage = "Notification [" + this.value + "]: " + message; diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts index 4d353d3a38a..eb517919dfc 100644 --- a/ui/src/app/shared/device/device.ts +++ b/ui/src/app/shared/device/device.ts @@ -23,6 +23,7 @@ export class Log { message: string; } +// TODO rename to Edge export class Device { constructor( diff --git a/ui/src/environments/environment.ts b/ui/src/environments/environment.ts index 0a8e48d4c13..bdde120aad8 100644 --- a/ui/src/environments/environment.ts +++ b/ui/src/environments/environment.ts @@ -4,11 +4,11 @@ import { DefaultTypes } from "../app/shared/service/defaulttypes"; class DefaultEnvironment extends Environment { public readonly production = false; // For OpenEMS Edge - public readonly url = "ws://" + location.hostname + ":8085"; - public readonly backend: DefaultTypes.Backend = "OpenEMS Edge"; + // public readonly url = "ws://" + location.hostname + ":8085"; + // public readonly backend: DefaultTypes.Backend = "OpenEMS Edge"; // For OpenEMS Backend - // public readonly url = "ws://" + location.hostname + ":8088"; - // public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; + public readonly url = "ws://" + location.hostname + ":8078"; + public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; public debugMode = true; } From 523725315e0f843b411e99d34f0b314789c1358f Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sat, 3 Mar 2018 11:55:01 +0100 Subject: [PATCH 116/156] Cleaning + handling TODOs --- edge/src/io/openems/api/bridge/Bridge.java | 3 +- edge/src/io/openems/api/channel/Channel.java | 9 +- .../io/openems/api/channel/ReadChannel.java | 12 +- .../io/openems/api/channel/WriteChannel.java | 4 +- .../thingstate/ThingStateChannels.java | 3 +- .../api/persistence/QueryablePersistence.java | 4 +- edge/src/io/openems/api/thing/Thing.java | 7 - edge/src/io/openems/core/Address.java | 33 --- edge/src/io/openems/core/Config.java | 5 +- edge/src/io/openems/core/Databus.java | 216 ++++++++---------- edge/src/io/openems/core/ThingRepository.java | 2 +- .../core/utilities/AbstractWorker.java | 1 - .../io/openems/core/utilities/BitUtils.java | 16 +- .../openems/core/utilities/LinuxCommand.java | 2 +- .../websocket/EdgeCurrentDataWorker.java | 15 +- .../session/WebsocketApiSession.java | 11 - .../session/WebsocketApiSessionData.java | 41 ---- .../session/WebsocketApiSessionManager.java | 58 ----- .../fenecon/FeneconPersistence.java | 2 +- .../influxdb/InfluxdbPersistence.java | 2 +- .../influxdb/InfluxdbQueryWrapper.java | 8 +- .../impl/provider/EdgeWebsocketServer.java | 23 +- .../backend/timedata/influx/Influx.java | 3 +- io.openems.backend.uiwebsocket.api/bnd.bnd | 3 +- .../uiwebsocket/api/UiWebsocketService.java | 4 +- .../impl/provider/UiWebsocket.java | 3 +- .../impl/provider/UiWebsocketServer.java | 45 ++-- .../io/openems/common/session/Session.java | 28 --- .../openems/common/session/SessionData.java | 7 - .../common/session/SessionManager.java | 126 ---------- .../openems/common/types/ChannelAddress.java | 17 ++ .../io/openems/common/utils/JsonUtils.java | 27 --- .../common/websocket/Notification.java | 8 +- 33 files changed, 213 insertions(+), 535 deletions(-) delete mode 100644 edge/src/io/openems/core/Address.java delete mode 100644 edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSession.java delete mode 100644 edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionData.java delete mode 100644 edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java delete mode 100644 io.openems.common/src/io/openems/common/session/Session.java delete mode 100644 io.openems.common/src/io/openems/common/session/SessionData.java delete mode 100644 io.openems.common/src/io/openems/common/session/SessionManager.java diff --git a/edge/src/io/openems/api/bridge/Bridge.java b/edge/src/io/openems/api/bridge/Bridge.java index 1aa668e0009..498ace46048 100644 --- a/edge/src/io/openems/api/bridge/Bridge.java +++ b/edge/src/io/openems/api/bridge/Bridge.java @@ -254,8 +254,7 @@ public final void run() { try { Thread.sleep(20l); } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.warn("Bridge sleep got interrupted: " + e.getMessage()); } } } diff --git a/edge/src/io/openems/api/channel/Channel.java b/edge/src/io/openems/api/channel/Channel.java index e8614f6e007..4e6d41db3ec 100644 --- a/edge/src/io/openems/api/channel/Channel.java +++ b/edge/src/io/openems/api/channel/Channel.java @@ -30,14 +30,19 @@ import io.openems.common.exceptions.NotImplementedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.session.Role; +import io.openems.common.types.ChannelAddress; -//TODO change to generic to use Generic ChannelUpdate/ChangeListener public interface Channel { public String id(); public Thing parent(); - public String address(); + /** + * Gets the channel address for this Channel (e.g. "ess0/Soc") + * + * @return + */ + public ChannelAddress address(); /** * Register a listener for update events on this Channel diff --git a/edge/src/io/openems/api/channel/ReadChannel.java b/edge/src/io/openems/api/channel/ReadChannel.java index 8583b956ab6..300ff3d8b91 100644 --- a/edge/src/io/openems/api/channel/ReadChannel.java +++ b/edge/src/io/openems/api/channel/ReadChannel.java @@ -45,14 +45,13 @@ import io.openems.core.Databus; import io.openems.core.utilities.InjectionUtils; - public class ReadChannel implements Channel, Comparable> { protected final Logger log; private final String id; private final Thing parent; private Optional value = Optional.empty(); - private Optional> type = Optional.empty(); // TODO remove type in favour of annotation/channelDoc + private Optional> type = Optional.empty(); private Optional channelDocOpt = Optional.empty(); protected Optional delta = Optional.empty(); @@ -157,15 +156,8 @@ public String id() { return id; } - /** - * TODO Use channelAddress() instead - */ @Override - public String address() { - return parent.id() + "/" + id; - } - - public ChannelAddress channelAddress() { + public ChannelAddress address() { return new ChannelAddress(parent.id(), id); } diff --git a/edge/src/io/openems/api/channel/WriteChannel.java b/edge/src/io/openems/api/channel/WriteChannel.java index d4dad7f2e9d..516a63893d5 100644 --- a/edge/src/io/openems/api/channel/WriteChannel.java +++ b/edge/src/io/openems/api/channel/WriteChannel.java @@ -80,7 +80,7 @@ public Optional writeMin() { if (min == null || (channelMin != null && channelMin instanceof Comparable && ((Comparable) channelMin).compareTo(min) > 0)) { if (channelMin instanceof Long && (Long) channelMin == 0) { - // TODO. This is a hack. If the storage system is stopped, channel is limited to 0 and making us unable + // This is a hack. If the storage system is stopped, channel is limited to 0 and making us unable // to start the system again } else { min = channelMin; @@ -112,7 +112,7 @@ public Optional writeMax() { if (max == null || (channelMax != null && channelMax instanceof Comparable && ((Comparable) channelMax).compareTo(max) < 0)) { if (channelMax instanceof Long && (Long) channelMax == 0) { - // TODO. This is a hack. If the storage system is stopped, channel is limited to 0 and making us unable + // This is a hack. If the storage system is stopped, channel is limited to 0 and making us unable // to start the system again } else { max = channelMax; diff --git a/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java b/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java index 058764d5082..a3c18d003d0 100644 --- a/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java +++ b/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java @@ -12,13 +12,14 @@ import io.openems.api.channel.ThingStateChannel; import io.openems.api.exception.ConfigException; import io.openems.api.thing.Thing; +import io.openems.common.types.ChannelAddress; public class ThingStateChannels extends ReadChannel implements ChannelChangeListener { private List warningChannels; private List faultChannels; private List childChannels; - private Set channelNames; + private Set channelNames; public ThingStateChannels(Thing parent){ super("State", parent); diff --git a/edge/src/io/openems/api/persistence/QueryablePersistence.java b/edge/src/io/openems/api/persistence/QueryablePersistence.java index d9e12096bb3..ca8dc35950c 100644 --- a/edge/src/io/openems/api/persistence/QueryablePersistence.java +++ b/edge/src/io/openems/api/persistence/QueryablePersistence.java @@ -31,12 +31,12 @@ public abstract class QueryablePersistence extends Persistence implements TimedataService { @Override public Optional getChannelValue(int edgeId, ChannelAddress channelAddress) { - // TODO implement after migration to OSGi + // FIXME implement after migration to OSGi return null; } @Override public void write(int edgeId, JsonObject jData) throws OpenemsException { - // TODO implement after migration to OSGi + // FIXME implement after migration to OSGi } } diff --git a/edge/src/io/openems/api/thing/Thing.java b/edge/src/io/openems/api/thing/Thing.java index 8e06b542e9e..b34b78de8ce 100644 --- a/edge/src/io/openems/api/thing/Thing.java +++ b/edge/src/io/openems/api/thing/Thing.java @@ -59,11 +59,4 @@ public default ReadChannel[] getWarningChannels(){ ThingStateChannels stateChannel = getStateChannel(); return stateChannel.getWarningChannels().toArray(new ReadChannel[stateChannel.getWarningChannels().size()]); } - - /** - * Sets the Thing annotation. This method is called after the thing was initialized via init() - * - * @throws OpenemsException - */ - // TODO public void setThingDoc(ThingDoc channelDoc) throws OpenemsException; } diff --git a/edge/src/io/openems/core/Address.java b/edge/src/io/openems/core/Address.java deleted file mode 100644 index 9a7f3792261..00000000000 --- a/edge/src/io/openems/core/Address.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.openems.core; - -// TODO move this to common package (types/ChannelAddress) -public class Address { - private final String thingId; - private final String channelId; - - public Address(String thingId, String channelId) { - super(); - this.thingId = thingId; - this.channelId = channelId; - } - - public String getThingId() { - return thingId; - } - - public String getChannelId() { - return channelId; - } - - @Override - public String toString() { - return thingId + "/" + channelId; - } - - public static Address fromString(String address) { - String[] addressArray = address.split("/"); - String thingId = addressArray[0]; - String channelId = addressArray[1]; - return new Address(thingId, channelId); - } -} diff --git a/edge/src/io/openems/core/Config.java b/edge/src/io/openems/core/Config.java index 560831d5136..a065bfd0c8c 100644 --- a/edge/src/io/openems/core/Config.java +++ b/edge/src/io/openems/core/Config.java @@ -53,12 +53,12 @@ import io.openems.api.device.Device; import io.openems.api.doc.ThingDoc; import io.openems.api.exception.ConfigException; -import io.openems.common.exceptions.NotImplementedException; import io.openems.api.exception.ReflectionException; import io.openems.api.persistence.Persistence; import io.openems.api.scheduler.Scheduler; import io.openems.api.security.User; import io.openems.api.thing.Thing; +import io.openems.common.exceptions.NotImplementedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; @@ -465,7 +465,7 @@ public JsonObject getSchedulerJson(ConfigFormat format, Role role) throws NotImp jControllers.add(ConfigUtils.getAsJsonElement(controller, format, role)); } jScheduler.add("controllers", jControllers); - break; // TODO only one Scheduler supported + break; } return jScheduler; } @@ -497,7 +497,6 @@ private JsonObject getUsersJson() { * @return * @throws NotImplementedException */ - // TODO make use of language tag Enum public synchronized JsonObject getJson(ConfigFormat format, Role role, String language) throws NotImplementedException { JsonObject jConfig = new JsonObject(); diff --git a/edge/src/io/openems/core/Databus.java b/edge/src/io/openems/core/Databus.java index 760803ec25a..ea77dc80641 100644 --- a/edge/src/io/openems/core/Databus.java +++ b/edge/src/io/openems/core/Databus.java @@ -1,117 +1,99 @@ -/******************************************************************************* - * 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.core; - -import java.util.Optional; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.channel.Channel; -import io.openems.api.channel.ChannelChangeListener; -import io.openems.api.channel.ChannelUpdateListener; -import io.openems.api.channel.ConfigChannel; -import io.openems.api.channel.ReadChannel; -import io.openems.api.device.nature.charger.ChargerNature; -import io.openems.api.device.nature.meter.AsymmetricMeterNature; -import io.openems.api.device.nature.meter.SymmetricMeterNature; -import io.openems.common.types.ChannelAddress; - -public class Databus implements ChannelUpdateListener, ChannelChangeListener { - private final static Logger log = LoggerFactory.getLogger(Databus.class); - - private static Databus instance; - - public static synchronized Databus getInstance() { - if (Databus.instance == null) { - Databus.instance = new Databus(); - } - return Databus.instance; - } - - private final ThingRepository thingRepository; - - private Databus() { - thingRepository = ThingRepository.getInstance(); - } - - @Override - public void channelUpdated(Channel channel, Optional newValue) { - log.debug("Channel [" + channel.address() + "] updated: " + newValue); - // Call Persistence-Workers - if (channel instanceof ReadChannel && !(channel instanceof ConfigChannel)) { - thingRepository.getPersistences().forEach(persistence -> { - if (persistence instanceof ChannelUpdateListener) { - ((ChannelUpdateListener) persistence).channelUpdated(channel, newValue); - } - }); - } - - // Update min/max values of meter - if (channel.parent() instanceof AsymmetricMeterNature && (channel.id().equals("ActivePowerL1") - || channel.id().equals("ActivePowerL2") || channel.id().equals("ActivePowerL3"))) { - ((AsymmetricMeterNature) channel.parent()).updateMinMaxAsymmetricActivePower(); - } else if (channel.parent() instanceof SymmetricMeterNature && channel.id().equals("ActivePower")) { - ((SymmetricMeterNature) channel.parent()).updateMinMaxSymmetricActivePower(); - } else if (channel.parent() instanceof ChargerNature && channel.id().equals("ActualPower")) { - ((ChargerNature) channel.parent()).updateMaxChargerActualPower(); - } - - } - - @Override - public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - log.debug("Channel [" + channel.address() + "] changed from " + oldValue + " to " + newValue); - // Call Persistence-Workers - if (!(channel instanceof ConfigChannel)) { - thingRepository.getPersistences().forEach(persistence -> { - if (persistence instanceof ChannelChangeListener) { - ((ChannelChangeListener) persistence).channelChanged(channel, newValue, oldValue); - } - }); - } - } - - @Deprecated - // TODO check permissions - public Optional getValue(String thingId, String channelId) { - Optional channelOpt = thingRepository.getChannel(thingId, channelId); - if (channelOpt.isPresent()) { - return this.getValue(channelOpt.get()); - } else { - return Optional.empty(); - } - } - - @Deprecated - public Optional getValue(ChannelAddress channelAddress) { - return this.getValue(channelAddress.getThingId(), channelAddress.getChannelId()); - } - - // TODO this should be in a Utils class or "default" method of Channel - public Optional getValue(Channel channel) { - if (channel instanceof ReadChannel) { - return ((ReadChannel) channel).valueOptional(); - } else { - return Optional.empty(); - } - } -} +/******************************************************************************* + * 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.core; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ChannelChangeListener; +import io.openems.api.channel.ChannelUpdateListener; +import io.openems.api.channel.ConfigChannel; +import io.openems.api.channel.ReadChannel; +import io.openems.api.device.nature.charger.ChargerNature; +import io.openems.api.device.nature.meter.AsymmetricMeterNature; +import io.openems.api.device.nature.meter.SymmetricMeterNature; + +public class Databus implements ChannelUpdateListener, ChannelChangeListener { + private final static Logger log = LoggerFactory.getLogger(Databus.class); + + private static Databus instance; + + public static synchronized Databus getInstance() { + if (Databus.instance == null) { + Databus.instance = new Databus(); + } + return Databus.instance; + } + + private final ThingRepository thingRepository; + + private Databus() { + thingRepository = ThingRepository.getInstance(); + } + + @Override + public void channelUpdated(Channel channel, Optional newValue) { + log.debug("Channel [" + channel.address() + "] updated: " + newValue); + // Call Persistence-Workers + if (channel instanceof ReadChannel && !(channel instanceof ConfigChannel)) { + thingRepository.getPersistences().forEach(persistence -> { + if (persistence instanceof ChannelUpdateListener) { + ((ChannelUpdateListener) persistence).channelUpdated(channel, newValue); + } + }); + } + + // Update min/max values of meter + if (channel.parent() instanceof AsymmetricMeterNature && (channel.id().equals("ActivePowerL1") + || channel.id().equals("ActivePowerL2") || channel.id().equals("ActivePowerL3"))) { + ((AsymmetricMeterNature) channel.parent()).updateMinMaxAsymmetricActivePower(); + } else if (channel.parent() instanceof SymmetricMeterNature && channel.id().equals("ActivePower")) { + ((SymmetricMeterNature) channel.parent()).updateMinMaxSymmetricActivePower(); + } else if (channel.parent() instanceof ChargerNature && channel.id().equals("ActualPower")) { + ((ChargerNature) channel.parent()).updateMaxChargerActualPower(); + } + + } + + @Override + public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { + log.debug("Channel [" + channel.address() + "] changed from " + oldValue + " to " + newValue); + // Call Persistence-Workers + if (!(channel instanceof ConfigChannel)) { + thingRepository.getPersistences().forEach(persistence -> { + if (persistence instanceof ChannelChangeListener) { + ((ChannelChangeListener) persistence).channelChanged(channel, newValue, oldValue); + } + }); + } + } + + public Optional getValue(Channel channel) { + if (channel instanceof ReadChannel) { + return ((ReadChannel) channel).valueOptional(); + } else { + return Optional.empty(); + } + } +} diff --git a/edge/src/io/openems/core/ThingRepository.java b/edge/src/io/openems/core/ThingRepository.java index 07cc649b69a..36314583682 100644 --- a/edge/src/io/openems/core/ThingRepository.java +++ b/edge/src/io/openems/core/ThingRepository.java @@ -283,7 +283,7 @@ public synchronized void removeThing(Thing thing) { // Remove Listener thing.removeListener(this); - // TODO further cleaning if required + for (ThingsChangedListener listener : thingListeners) { listener.thingChanged(thing, Action.REMOVE); } diff --git a/edge/src/io/openems/core/utilities/AbstractWorker.java b/edge/src/io/openems/core/utilities/AbstractWorker.java index b2bf770ab25..25e00d48322 100644 --- a/edge/src/io/openems/core/utilities/AbstractWorker.java +++ b/edge/src/io/openems/core/utilities/AbstractWorker.java @@ -104,7 +104,6 @@ public String id() { @Override public void interrupt() { - // TODO Auto-generated method stub super.interrupt(); } diff --git a/edge/src/io/openems/core/utilities/BitUtils.java b/edge/src/io/openems/core/utilities/BitUtils.java index 90020dc60e4..f131160c01c 100644 --- a/edge/src/io/openems/core/utilities/BitUtils.java +++ b/edge/src/io/openems/core/utilities/BitUtils.java @@ -30,8 +30,8 @@ public static int getBitLength(Class type) throws NotImplementedException { case BOOLEAN: return BITS_BOOLEAN; - case DOUBLE: // TODO - case INET_4_ADDRESS: // TODO + case DOUBLE: // TODO implement DOUBLE bitlength + case INET_4_ADDRESS: // TODO implement INET_4_ADDRESS bitlength case STRING: case LONG_ARRAY: case JSON_ARRAY: @@ -56,9 +56,9 @@ public static byte[] toBytes(Object value) throws NotImplementedException { case LONG: return ByteBuffer.allocate(BYTES_LONG).order(BYTE_ORDER).putLong((Long) value).array(); - case BOOLEAN: // TODO put boolean value in a byte - case DOUBLE: // TODO - case INET_4_ADDRESS: // TODO + case BOOLEAN: // TODO implmement BOOLEAN toBytes (put boolean value in a byte) + case DOUBLE: // TODO implement DOUBLE toBytes + case INET_4_ADDRESS: // TODO implement INET_4_ADDRESS toBytes case STRING: case LONG_ARRAY: case JSON_ARRAY: @@ -92,9 +92,9 @@ public static Object toObject(Class type, byte... value) throws NotImplemente b.rewind(); return b.getLong(); } - case BOOLEAN: // TODO put boolean value in a byte - case DOUBLE: // TODO - case INET_4_ADDRESS: // TODO + case BOOLEAN: // TODO implement BOOLEAN toObject (put boolean value in a byte) + case DOUBLE: // TODO implement DOUBLE toObject + case INET_4_ADDRESS: // TODO implement INET_4_ADDRESS toObject case STRING: case LONG_ARRAY: case JSON_ARRAY: diff --git a/edge/src/io/openems/core/utilities/LinuxCommand.java b/edge/src/io/openems/core/utilities/LinuxCommand.java index 0b0eaf6abb1..a50613311b2 100644 --- a/edge/src/io/openems/core/utilities/LinuxCommand.java +++ b/edge/src/io/openems/core/utilities/LinuxCommand.java @@ -32,7 +32,7 @@ public static String execute(String password, String command, boolean background try { Process proc = Runtime.getRuntime().exec(new String[] { "/bin/bash", "-c", "echo " + password + " | /usr/bin/sudo -Sk -p '' -- /bin/bash -c -- '" + command + "' 2>&1" }); - // TODO enfoce password when already running as root + // TODO improve enforcement of password when already running as root // this complex argument tries to enforce the sudo password and to avoid security vulnerabilites. // get stdout and stderr diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java index bb42461c359..b685e93aeaa 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java @@ -12,6 +12,7 @@ import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.Log; import io.openems.common.websocket.CurrentDataWorker; import io.openems.core.Databus; import io.openems.core.ThingRepository; @@ -35,21 +36,25 @@ public EdgeCurrentDataWorker(EdgeWebsocketHandler edgeWebsocketHandler, WebSocke @Override protected Optional getChannelValue(ChannelAddress channelAddress) { - // TODO rename getChannel() to getChannelOpt - // TODO create new getChannel() that throws an error if not existing Optional channelOpt = thingRepository.getChannel(channelAddress); if (channelOpt.isPresent()) { Channel channel = channelOpt.get(); try { channel.assertReadAllowed(this.role); + } catch (AccessDeniedException e) { + Log.warn("Channel [" + channelAddress + "] access is not allowed by Role [" + this.role + "]: " + + e.getMessage()); + return Optional.empty(); + } + try { return Optional.ofNullable(JsonUtils.getAsJsonElement(databus.getValue(channel).orElse(null))); - } catch (AccessDeniedException | NotImplementedException e) { + } catch (NotImplementedException e) { + Log.warn("Channel [" + channelAddress + "] value conversion failed: " + e.getMessage()); return Optional.empty(); - // TODO log error message: not allowed - or conversion not implemented } } else { + Log.warn("Channel [" + channelAddress + "] not found"); return Optional.empty(); - // TODO log error message: channel not found } } } diff --git a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSession.java b/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSession.java deleted file mode 100644 index 8827c6469e3..00000000000 --- a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSession.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.openems.impl.controller.api.websocket.session; - -import io.openems.common.session.Session; - -public class WebsocketApiSession extends Session { - - protected WebsocketApiSession(String token, WebsocketApiSessionData data) { - super(token, data); - } - -} diff --git a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionData.java b/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionData.java deleted file mode 100644 index 1979c2eaf96..00000000000 --- a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionData.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.openems.impl.controller.api.websocket.session; - -import com.google.gson.JsonObject; - -import io.openems.api.security.User; -import io.openems.common.session.SessionData; -import io.openems.core.utilities.websocket.EdgeWebsocketHandler; - -public class WebsocketApiSessionData extends SessionData { - private final User user; - private final EdgeWebsocketHandler websocketHandler; - - public WebsocketApiSessionData(User user, EdgeWebsocketHandler websocketHandler) { - this.user = user; - this.websocketHandler = websocketHandler; - } - - public User getUser() { - return user; - } - - public String getRole() { - return this.user.getName(); - } - - public EdgeWebsocketHandler getWebsocketHandler() { - return websocketHandler; - } - - @Override - public String toString() { - return "WebsocketApiSessionData [user=" + user + "]"; - } - - @Override - public JsonObject toJsonObject() { - JsonObject j = new JsonObject(); - // TODO Auto-generated method stub - return j; - } -} diff --git a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java b/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java deleted file mode 100644 index ce06f701af5..00000000000 --- a/edge/src/io/openems/impl/controller/api/websocket/session/WebsocketApiSessionManager.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.openems.impl.controller.api.websocket.session; - -import java.util.Optional; - -import org.java_websocket.WebSocket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.security.User; -import io.openems.common.session.SessionManager; -import io.openems.core.utilities.api.ApiWorker; -import io.openems.core.utilities.websocket.EdgeWebsocketHandler; - -public class WebsocketApiSessionManager extends SessionManager { - - private static Logger log = LoggerFactory.getLogger(WebsocketApiSessionManager.class); - - @Override - public WebsocketApiSession _createNewSession(String token, WebsocketApiSessionData data) { - return new WebsocketApiSession(token, data); - } - - private Optional createSessionForUser(Optional userOpt, WebSocket websocket, - ApiWorker apiWorker) { - if (userOpt.isPresent()) { - User user = userOpt.get(); - WebsocketApiSessionData data = new WebsocketApiSessionData(user, - new EdgeWebsocketHandler(websocket, apiWorker)); - String token = generateToken(); - WebsocketApiSession session = createNewSession(token, data); - return Optional.of(session); - } - return Optional.empty(); - } - - public Optional authByUserPassword(String username, String password, WebSocket websocket, - ApiWorker apiWorker) { - Optional user = User.authenticate(username, password); - return createSessionForUser(user, websocket, apiWorker); - } - - public Optional authByPassword(String password, WebSocket websocket, ApiWorker apiWorker) { - Optional user = User.authenticate(password); - return createSessionForUser(user, websocket, apiWorker); - } - - public Optional authBySession(String token) { - Optional sessionOpt = getSessionByToken(token); - if (sessionOpt.isPresent()) { - WebsocketApiSession session = sessionOpt.get(); - log.info("User[" + session.getData().getUser().getName() + "] authenticated by " + // - "session[" + token + "]."); - } else { - log.info("Authentication by session failed."); - } - return sessionOpt; - } -} diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index 04e525d801b..6d7a518ec9e 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -349,7 +349,7 @@ private void addChannelValueToQueue(Channel channel, Optional valueOpt) { // Add timestamp + value to queue synchronized (queue) { - queue.put(readChannel.channelAddress(), fieldValue); + queue.put(readChannel.address(), fieldValue); } } diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java index 3e8a6627367..9ef151fa433 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java @@ -101,7 +101,7 @@ public void channelUpdated(Channel channel, Optional newValue) { return; } Object value = newValue.get(); - String field = readChannel.address(); + String field = readChannel.address().toString(); FieldValue fieldValue; // TODO merge this with io.openems.backend.timedata.influx.addChannelToBuilder() if (value instanceof Number) { diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java index 80cd0b5ac09..d2840baedba 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java @@ -22,8 +22,8 @@ import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; -import io.openems.core.Address; public class InfluxdbQueryWrapper { @@ -91,12 +91,12 @@ private static JsonArray queryData(InfluxDB influxdb, Optional fems, Zo if (seriess != null) { for (Series series : seriess) { // create thing/channel index - ArrayList
        addressIndex = new ArrayList<>(); + ArrayList addressIndex = new ArrayList<>(); for (String column : series.getColumns()) { if (column.equals("time")) { continue; } - addressIndex.add(Address.fromString(column)); + addressIndex.add(ChannelAddress.fromString(column)); } // first: create empty timestamp objects for (List values : series.getValues()) { @@ -125,7 +125,7 @@ private static JsonArray queryData(InfluxDB influxdb, Optional fems, Zo for (int columnIndex = 1; columnIndex < series.getColumns().size(); columnIndex++) { for (int timeIndex = 0; timeIndex < series.getValues().size(); timeIndex++) { Double value = (Double) series.getValues().get(timeIndex).get(columnIndex); - Address address = addressIndex.get(columnIndex - 1); + ChannelAddress address = addressIndex.get(columnIndex - 1); j.get(timeIndex).getAsJsonObject().get("channels").getAsJsonObject() .get(address.getThingId()).getAsJsonObject() .addProperty(address.getChannelId(), value); diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 83a698a9a34..6c0312a6796 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -22,6 +22,8 @@ import io.openems.common.utils.StringUtils; import io.openems.common.websocket.AbstractWebsocketServer; import io.openems.common.websocket.DefaultMessages; +import io.openems.common.websocket.LogBehaviour; +import io.openems.common.websocket.Notification; import io.openems.common.websocket.WebSocketUtils; public class EdgeWebsocketServer extends AbstractWebsocketServer { @@ -94,7 +96,8 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { JsonObject jReply = DefaultMessages.openemsConnectionFailedReply(e.getMessage()); WebSocketUtils.sendOrLogError(websocket, jReply); // close websocket - websocket.closeConnection(CloseFrame.REFUSE, "OpenEMS connection failed. Apikey [" + apikey + "]"); + websocket.closeConnection(CloseFrame.REFUSE, + "OpenEMS connection failed. Apikey [" + apikey + "]. Error: " + e.getMessage()); } } @@ -107,6 +110,9 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { // get edgeIds from websocket int[] edgeIds = websocket.getAttachment(); + // get MessageId from message + JsonObject jMessageId = JsonUtils.getAsOptionalJsonObject(jMessage, "messageId").orElse(new JsonObject()); + /* * Config? -> store in Metadata */ @@ -119,18 +125,24 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { edge = this.parent.metadataService.getEdge(edgeId); edge.setConfig(jConfig); } catch (OpenemsException e) { - log.warn(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.METADATA_ERROR, e.getMessage()); } } return; } /* - * Is this a reply? -> forward to Browser + * Is this a reply? -> forward to UI */ if (jMessage.has("messageId")) { for (int edgeId : edgeIds) { - this.parent.uiWebsocketService.handleEdgeReply(edgeId, jMessage); + try { + this.parent.uiWebsocketService.handleEdgeReply(edgeId, jMessage); + } catch (OpenemsException e) { + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.EDGE_UNABLE_TO_FORWARD, "ID:" + edgeId, e.getMessage()); + } } return; } @@ -148,7 +160,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { * Unknown message */ for (String edgeName : getEdgeNames(edgeIds)) { - log.warn("Edge [" + edgeName + "] unknown message: " + StringUtils.toShortString(jMessage, 100)); + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.UNKNOWN_MESSAGE, edgeName, StringUtils.toShortString(jMessage, 100)); } } diff --git a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java index a672f174cc4..f5e3503c630 100644 --- a/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx.provider/src/io/openems/backend/timedata/influx/Influx.java @@ -131,7 +131,7 @@ private void writeData(int influxId, TreeBasedTable data) /** * Writes data to old database for old Mini monitoring * - * TODO remove after full migration + * XXX remove after full migration * * @param device * @param data @@ -318,7 +318,6 @@ public void write(int edgeId, JsonObject jData) throws OpenemsException { writeData(influxId, data); // Hook to continue writing data to old Mini monitoring - // TODO remove after full migration if (edge.getProducttype().equals("MiniES 3-3")) { writeDataToOldMiniMonitoring(edge, influxId, data); } diff --git a/io.openems.backend.uiwebsocket.api/bnd.bnd b/io.openems.backend.uiwebsocket.api/bnd.bnd index 74346fb8382..a7df1ba56f1 100644 --- a/io.openems.backend.uiwebsocket.api/bnd.bnd +++ b/io.openems.backend.uiwebsocket.api/bnd.bnd @@ -13,7 +13,8 @@ Require-Capability: \ -buildpath: \ osgi.enroute.base.api;version=2.1,\ - com.google.gson + com.google.gson,\ + io.openems.common;version=latest -testpath: \ osgi.enroute.junit.wrapper;version=4.12, \ diff --git a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java index c589adb8871..bed0f5612d1 100644 --- a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java +++ b/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocketService.java @@ -4,9 +4,11 @@ import com.google.gson.JsonObject; +import io.openems.common.exceptions.OpenemsException; + @ProviderType public interface UiWebsocketService { - public abstract void handleEdgeReply(int edgeId, JsonObject jMessage); + public abstract void handleEdgeReply(int edgeId, JsonObject jMessage) throws OpenemsException; } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java index 7d5f8a6dd8b..28fd2363c29 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocket.java @@ -18,6 +18,7 @@ import io.openems.backend.metadata.api.MetadataService; import io.openems.backend.timedata.api.TimedataService; import io.openems.backend.uiwebsocket.api.UiWebsocketService; +import io.openems.common.exceptions.OpenemsException; @Designate(ocd = UiWebsocket.Config.class, factory = false) @Component(name = "UiWebsocket", configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true) @@ -90,7 +91,7 @@ private void startServer(int port) { } @Override - public void handleEdgeReply(int edgeId, JsonObject jMessage) { + public void handleEdgeReply(int edgeId, JsonObject jMessage) throws OpenemsException { this.server.handleEdgeReply(edgeId, jMessage); } } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 9e20a61e0ca..33ccdcd2171 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -49,7 +49,6 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { throw new OpenemsException("Session-ID is missing in handshake"); } user = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); - // TODO fix bug in Odoo that is not reliably returning all configured devices } catch (OpenemsException e) { // send connection failed to browser WebSocketUtils.sendOrLogError(websocket, DefaultMessages.uiConnectionFailedReply()); @@ -79,8 +78,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { jEdge.addProperty("role", role.toString()); jEdges.add(jEdge); } catch (OpenemsException e) { - // TODO handle error - log.warn(e.getMessage()); + log.warn("Unable to get Edge from MetadataService [ID:" + edgeId + "]: " + e.getMessage()); } } log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); @@ -91,7 +89,8 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { @Override protected void _onError(WebSocket websocket, Exception ex) { WebsocketData data = websocket.getAttachment(); - log.info("User [" + getUserName(data) + "] websocket error: " + ex.getMessage()); + log.warn("User [" + getUserName(data) + "] websocket error. " + ex.getClass().getSimpleName() + ": " + + ex.getMessage()); } @Override @@ -159,6 +158,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { Optional jHistoricDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "historicData"); if (jHistoricDataOpt.isPresent()) { JsonObject jHistoricData = jHistoricDataOpt.get(); + log.info("User [" + user.getName() + "] queried historic data for Edge [" + edge.getName() + "]: " + + StringUtils.toShortString(jHistoricData, 50)); this.historicData(websocket, jMessageId, edgeId, jHistoricData); return; } @@ -169,8 +170,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { Optional jCurrentDataOpt = JsonUtils.getAsOptionalJsonObject(jMessage, "currentData"); if (jCurrentDataOpt.isPresent()) { JsonObject jCurrentData = jCurrentDataOpt.get(); - log.info("User [" + user.getName() + "] subscribed to current data for device [" + edge.getName() - + "]: " + StringUtils.toShortString(jCurrentData, 50)); + log.info("User [" + user.getName() + "] subscribed to current data for Edge [" + edge.getName() + "]: " + + StringUtils.toShortString(jCurrentData, 50)); this.currentData(websocket, data, jMessageId, edgeId, jCurrentData); return; } @@ -186,6 +187,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { /* * Query current config */ + log.info("User [" + user.getName() + "] queried config for Edge [" + edge.getName() + "]: " + + StringUtils.toShortString(jConfig, 50)); JsonObject jReply = DefaultMessages.configQueryReply(jMessageId, edge.getConfig()); WebSocketUtils.sendOrLogError(websocket, jReply); return; @@ -197,7 +200,7 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { */ if (jMessage.has("config") || jMessage.has("log") || jMessage.has("system")) { try { - log.info("User [" + user.getName() + "] Forward message to Edge [" + edge.getName() + "]: " + log.info("User [" + user.getName() + "] forward message to Edge [" + edge.getName() + "]: " + StringUtils.toShortString(jMessage, 100)); Optional roleOpt = user.getEdgeRole(edgeId); JsonObject j = DefaultMessages.prepareMessageForForwardToEdge(jMessage, data.getUuid(), roleOpt); @@ -239,8 +242,8 @@ private synchronized void currentData(WebSocket websocket, WebsocketData data, J data.setCurrentDataWorker(worker); } } catch (OpenemsException e) { - // TODO handle exception - log.warn(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.SUBSCRIBE_CURRENT_DATA_FAILED, "Edge [ID:" + edgeId + "] " + e.getMessage()); } } @@ -263,7 +266,7 @@ private void historicData(WebSocket websocket, JsonObject jMessageId, int edgeId } } catch (Exception e) { WebSocketUtils.sendNotificationOrLogError(websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, - Notification.UNABLE_TO_QUERY_HISTORIC_DATA, edgeId, e.getMessage()); + Notification.UNABLE_TO_QUERY_HISTORIC_DATA, "Edge [ID:" + edgeId + "] " + e.getMessage()); } } @@ -276,19 +279,15 @@ private String getUserName(WebsocketData data) { } } - public void handleEdgeReply(int edgeId, JsonObject jMessage) { - try { - JsonObject jMessageId = JsonUtils.getAsJsonObject(jMessage, "messageId"); - String backendId = JsonUtils.getAsString(jMessageId, "backend"); - WebSocket websocket = this.websocketsMap.get(UUID.fromString(backendId)); - if (websocket != null) { - JsonObject j = DefaultMessages.prepareMessageForForwardToUi(jMessage); - WebSocketUtils.send(websocket, j); - return; - } - throw new OpenemsException("No websocket found for UUID [" + backendId + "]"); - } catch (OpenemsException e) { - log.error("Unable to handle reply from Edge [ID:" + edgeId + "]: " + e.getMessage()); + public void handleEdgeReply(int edgeId, JsonObject jMessage) throws OpenemsException { + JsonObject jMessageId = JsonUtils.getAsJsonObject(jMessage, "messageId"); + String backendId = JsonUtils.getAsString(jMessageId, "backend"); + WebSocket websocket = this.websocketsMap.get(UUID.fromString(backendId)); + if (websocket != null) { + JsonObject j = DefaultMessages.prepareMessageForForwardToUi(jMessage); + WebSocketUtils.send(websocket, j); + return; } + throw new OpenemsException("No websocket found for UUID [" + backendId + "]"); } } diff --git a/io.openems.common/src/io/openems/common/session/Session.java b/io.openems.common/src/io/openems/common/session/Session.java deleted file mode 100644 index 829266059d7..00000000000 --- a/io.openems.common/src/io/openems/common/session/Session.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.openems.common.session; - -public class Session { - private final String token; - - /** - * store additional metadata to this session - */ - private final D data; - - protected Session(String token, D data) { - this.token = token; - this.data = data; - } - - public String getToken() { - return token; - } - - public D getData() { - return data; - } - - @Override - public String toString() { - return "Session [token=" + token + ", data=" + data + "]"; - } -} diff --git a/io.openems.common/src/io/openems/common/session/SessionData.java b/io.openems.common/src/io/openems/common/session/SessionData.java deleted file mode 100644 index bd2b6b1aff6..00000000000 --- a/io.openems.common/src/io/openems/common/session/SessionData.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.openems.common.session; - -import com.google.gson.JsonObject; - -public abstract class SessionData { - public abstract JsonObject toJsonObject(); -} diff --git a/io.openems.common/src/io/openems/common/session/SessionManager.java b/io.openems.common/src/io/openems/common/session/SessionManager.java deleted file mode 100644 index 2452d5f5224..00000000000 --- a/io.openems.common/src/io/openems/common/session/SessionManager.java +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.common.session; - -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.utils.SecureRandomSingleton; - -public abstract class SessionManager, D extends SessionData> { - - private final Logger log = LoggerFactory.getLogger(SessionManager.class); - private final static int SESSION_ID_LENGTH = 130; - - // TODO: invalidate old sessions in separate thread: call _removeSession to do - // so - private final Map sessions = new ConcurrentHashMap<>(); - - protected SessionManager() { - } - - public S createNewSession(String token, D data) { - S session = this._createNewSession(token, data); - this._putSession(token, session); - return session; - } - - public S createNewSession(D data) { - String token = this.generateToken(); - return this.createNewSession(token, data); - } - - public Optional getSessionByToken(String token) { - synchronized (this.sessions) { - return Optional.ofNullable(this.sessions.get(token)); - } - } - - public void removeSession(String token) { - synchronized (this.sessions) { - S session = this.sessions.get(token); - if (session != null) { - this._removeSession(token); - } - } - } - - public void removeSession(Session session) { - this.removeSession(session.getToken()); - } - - protected String generateToken() { - // Source: http://stackoverflow.com/a/41156 - SecureRandom sr = SecureRandomSingleton.getInstance(); - return new BigInteger(SESSION_ID_LENGTH, sr).toString(32); - } - - public Collection getSessions() { - return Collections.unmodifiableCollection(this.sessions.values()); - } - - /* - * Those methods are prone to be overwritten by inheritance - */ - /** - * Replies a Session object of type T - * - * @param token - * @param websocket - * @param data - * @return - */ - protected abstract S _createNewSession(String token, D data); - - /** - * This method is always called when adding a session to local database - * - * @param token - * @param session - */ - protected void _putSession(String token, S session) { - synchronized (this.sessions) { - if (this.sessions.containsKey(token)) { - log.warn("Session with token [" + token + "] already existed. Replacing with session [" + session + "]"); - } - this.sessions.put(token, session); - } - } - - /** - * This method is always called when removing a session from local database - * - * @param session - */ - protected void _removeSession(String token) { - synchronized (this.sessions) { - this.sessions.remove(token); - } - } -} diff --git a/io.openems.common/src/io/openems/common/types/ChannelAddress.java b/io.openems.common/src/io/openems/common/types/ChannelAddress.java index a5f805e9ef0..2a0aaa62e57 100644 --- a/io.openems.common/src/io/openems/common/types/ChannelAddress.java +++ b/io.openems.common/src/io/openems/common/types/ChannelAddress.java @@ -40,4 +40,21 @@ public static ChannelAddress fromString(String address) throws OpenemsException public int compareTo(ChannelAddress other) { return this.toString().compareTo(other.toString()); } + + @Override + public int hashCode() { + return this.toString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ChannelAddress other = (ChannelAddress) obj; + return this.toString().equals(other.toString()); + } } 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 30877a8a652..f5d7eb64e45 100644 --- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java +++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java @@ -7,7 +7,6 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; import java.util.Optional; import java.util.Set; @@ -24,7 +23,6 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelEnum; -// TODO use getAsOptional***() as basis for getAs***() to avoid unnecessary exceptions public class JsonUtils { public static boolean getAsBoolean(JsonElement jElement) throws OpenemsException { JsonPrimitive jPrimitive = getAsPrimitive(jElement); @@ -385,31 +383,6 @@ public static boolean hasElement(JsonElement j, String... paths) { return getMatchingElements(j, paths).size() > 0; } - /** - * Merges the second Object into the first object - * - * @param j1 - * @param j2 - * @return - */ - public static JsonObject merge(JsonObject j1, JsonObject j2) { - // TODO be smarter: merge down the tree - for (Entry entry : j2.entrySet()) { - j1.add(entry.getKey(), entry.getValue()); - } - return j1; - } - - public static Optional merge(Optional j1Opt, Optional j2Opt) { - if (j1Opt.isPresent() && j2Opt.isPresent()) { - return Optional.of(JsonUtils.merge(j1Opt.get(), j2Opt.get())); - } - if (j1Opt.isPresent()) { - return j1Opt; - } - return j2Opt; - } - /** * Parses a string to a JsonElement * diff --git a/io.openems.common/src/io/openems/common/websocket/Notification.java b/io.openems.common/src/io/openems/common/websocket/Notification.java index 1f2df2767a1..b09617cd275 100644 --- a/io.openems.common/src/io/openems/common/websocket/Notification.java +++ b/io.openems.common/src/io/openems/common/websocket/Notification.java @@ -14,9 +14,11 @@ public enum Notification { BACKEND_FORWARD_TO_EDGE_NOT_ALLOWED(108, NotificationType.ERROR, "Message forward to Edge [%s] is not allowed for user [%s]."), // BACKEND_UNABLE_TO_READ_EDGE_DETAILS(109, NotificationType.ERROR, "Unable to read details for Edge [ID:%s]: %s"), // - UNABLE_TO_QUERY_HISTORIC_DATA(110, NotificationType.ERROR, "Unable to query historic data for Edge [ID:%s]: %s"), // - BACKEND_UNABLE_TO_READ_USER_DETAILS(111, NotificationType.ERROR, "Unable to read details for User [ID:%s]"); // - + UNABLE_TO_QUERY_HISTORIC_DATA(110, NotificationType.ERROR, "Unable to query historic data: %s"), // + BACKEND_UNABLE_TO_READ_USER_DETAILS(111, NotificationType.ERROR, "Unable to read details for User [ID:%s]"), // + METADATA_ERROR(112, NotificationType.ERROR, "Metadata operation failed: %s"), // + UNKNOWN_MESSAGE(113, NotificationType.WARNING, "Unknown message. Source [%s]. Message: %s"), // + SUBSCRIBE_CURRENT_DATA_FAILED(114, NotificationType.ERROR, "Subscription to current data failed: %s"); private final int value; private final NotificationType status; From 023800b078e701a2c59ced93da50f0162335c81f Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 4 Mar 2018 20:15:17 +0100 Subject: [PATCH 117/156] Cleaning + handling TODOs --- .../api/persistence/QueryablePersistence.java | 9 +- .../websocket/EdgeCurrentDataWorker.java | 15 +- .../websocket/EdgeWebsocketHandler.java | 123 ++++---- .../utilities/websocket/Notification.java | 58 ---- .../api/modbustcp/MyProcessImage.java | 22 +- .../controller/api/modbustcp/MyRegister.java | 6 +- .../api/websocket/WebsocketApiServer.java | 36 ++- .../FixValueReactivePowerController.java | 4 +- .../ChargeLimitationController.java | 4 +- .../debuglog/DebugLogController.java | 4 - .../AvoidTotalDischargeController.java | 9 +- ...idTotalDischargeSocTimeLineController.java | 6 +- .../AvoidTotalDischargeController.java | 15 +- .../TimelineChargeController.java | 3 +- .../VoltageCharacteristicController.java | 6 +- .../testwrite/TestWriteController.java | 6 +- .../commercial/FeneconCommercialCharger.java | 1 - .../commercial/FeneconCommercialEss.java | 3 +- .../src/io/openems/impl/device/keba/Keba.java | 9 +- .../FeneconMiniConsumptionMeter.java | 2 - .../device/minireadonly/FeneconMiniEss.java | 9 - .../minireadonly/FeneconMiniGridMeter.java | 2 - .../FeneconMiniProductionMeter.java | 2 - .../impl/device/pro/FeneconProEss.java | 120 ++++--- .../device/simulator/SimulatorGridMeter.java | 28 +- .../simulator/SimulatorProductionMeter.java | 24 +- .../simulator/SimulatorSymmetricEss.java | 25 +- .../streetscooter/StreetscooterEss1.java | 3 - .../streetscooter/StreetscooterEss2.java | 3 - .../impl/device/system/SystemNature.java | 2 +- .../AsymmetricSymmetricCombinationEss.java | 1 - .../device/system/esscluster/EssCluster.java | 1 - .../system/esscluster/EssClusterNature.java | 3 +- .../system/metercluster/MeterCluster.java | 1 - .../fenecon/FeneconPersistence.java | 2 +- .../fenecon/ReconnectingWebsocket.java | 3 +- .../impl/persistence/influxdb/FieldValue.java | 63 ++-- .../influxdb/InfluxdbPersistence.java | 12 + .../influxdb/InfluxdbQueryWrapper.java | 4 +- .../influxdb/NumberFieldValue.java | 59 ++-- .../influxdb/StringFieldValue.java | 59 ++-- .../modbus/ModbusCoilWriteChannel.java | 79 +++-- .../simulator/SimulatorWriteChannel.java | 55 ++-- .../studer/internal/StuderConnection.java | 298 +++++++++--------- .../protocol/system/SystemDeviceNature.java | 2 - .../controller/ThermalPowerStationTest.java | 6 +- .../supplybusswitch/SupplyBusTest.java | 27 +- .../test/core/utils/AsymmetricPowerTest.java | 19 +- .../utils/channel/UnitTestReadChannel.java | 1 - .../utils/channel/UnitTestWriteChannel.java | 1 - .../UnitTestAsymmetricEssNature.java | 5 - .../UnitTestAsymmetricMeterNature.java | 8 - .../UnitTestSymmetricEssNature.java | 5 - .../UnitTestSymmetricMeterNature.java | 9 - .../impl/provider/UiWebsocketServer.java | 4 +- .../common/websocket/CurrentDataWorker.java | 2 +- .../common/websocket/DefaultMessages.java | 4 +- .../common/websocket/Notification.java | 10 +- 58 files changed, 587 insertions(+), 715 deletions(-) delete mode 100644 edge/src/io/openems/core/utilities/websocket/Notification.java diff --git a/edge/src/io/openems/api/persistence/QueryablePersistence.java b/edge/src/io/openems/api/persistence/QueryablePersistence.java index ca8dc35950c..841b676bad5 100644 --- a/edge/src/io/openems/api/persistence/QueryablePersistence.java +++ b/edge/src/io/openems/api/persistence/QueryablePersistence.java @@ -30,13 +30,8 @@ public abstract class QueryablePersistence extends Persistence implements TimedataService { @Override - public Optional getChannelValue(int edgeId, ChannelAddress channelAddress) { - // FIXME implement after migration to OSGi - return null; - } + public abstract Optional getChannelValue(int edgeId, ChannelAddress channelAddress); @Override - public void write(int edgeId, JsonObject jData) throws OpenemsException { - // FIXME implement after migration to OSGi - } + public abstract void write(int edgeId, JsonObject jData) throws OpenemsException; } diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java index b685e93aeaa..927233e6df7 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeCurrentDataWorker.java @@ -5,6 +5,7 @@ import org.java_websocket.WebSocket; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.openems.api.channel.Channel; import io.openems.common.exceptions.AccessDeniedException; @@ -12,8 +13,10 @@ import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; -import io.openems.common.utils.Log; import io.openems.common.websocket.CurrentDataWorker; +import io.openems.common.websocket.LogBehaviour; +import io.openems.common.websocket.Notification; +import io.openems.common.websocket.WebSocketUtils; import io.openems.core.Databus; import io.openems.core.ThingRepository; @@ -42,18 +45,20 @@ protected Optional getChannelValue(ChannelAddress channelAddress) { try { channel.assertReadAllowed(this.role); } catch (AccessDeniedException e) { - Log.warn("Channel [" + channelAddress + "] access is not allowed by Role [" + this.role + "]: " - + e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject(), LogBehaviour.WRITE_TO_LOG, + Notification.CHANNEL_ACCESS_DENIED, channelAddress, this.role); return Optional.empty(); } try { return Optional.ofNullable(JsonUtils.getAsJsonElement(databus.getValue(channel).orElse(null))); } catch (NotImplementedException e) { - Log.warn("Channel [" + channelAddress + "] value conversion failed: " + e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject(), LogBehaviour.WRITE_TO_LOG, + Notification.VALUE_CONVERSION_FAILED, channelAddress, e.getMessage()); return Optional.empty(); } } else { - Log.warn("Channel [" + channelAddress + "] not found"); + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject(), LogBehaviour.WRITE_TO_LOG, + Notification.CHANNEL_NOT_FOUND, channelAddress); return Optional.empty(); } } diff --git a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java index d720f12d1fa..9e4af4e2747 100644 --- a/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java +++ b/edge/src/io/openems/core/utilities/websocket/EdgeWebsocketHandler.java @@ -185,7 +185,8 @@ public final void onMessage(JsonObject jMessage) { try { this.log(role, jMessageId, jLogOpt.get()); } catch (OpenemsException e) { - log.error(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.UNABLE_TO_SUBSCRIBE_TO_LOG, e.getMessage()); } } @@ -197,8 +198,8 @@ public final void onMessage(JsonObject jMessage) { try { this.system(jMessageId, jSystemOpt.get(), role); } catch (OpenemsException e) { - // TODO create notification - log.error(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.UNABLE_TO_EXECUTE_SYSTEM_COMMAND, e.getMessage()); } } } @@ -212,7 +213,8 @@ private void historicData(JsonObject jMessageId, JsonObject jHistoricData) { break; } if (timedataSource == null) { - // TODO create notification that there is no datasource available + WebSocketUtils.sendNotificationOrLogError(this.websocket, new JsonObject(), LogBehaviour.WRITE_TO_LOG, + Notification.NO_TIMEDATA_SOURCE_AVAILABLE); return; } JsonArray jData; @@ -220,8 +222,8 @@ private void historicData(JsonObject jMessageId, JsonObject jHistoricData) { jData = timedataSource.queryHistoricData(jHistoricData); WebSocketUtils.send(this.websocket, DefaultMessages.historicDataQueryReply(jMessageId, jData)); } catch (OpenemsException e) { - // TODO notification - log.error(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(this.websocket, jMessageId, LogBehaviour.WRITE_TO_LOG, + Notification.UNABLE_TO_QUERY_HISTORIC_DATA, e.getMessage()); } return; } @@ -246,8 +248,8 @@ private synchronized void config(Role role, JsonObject jMessageId, Optional clazz = Class.forName(clazzName); + // if (Device.class.isAssignableFrom(clazz)) { + // // Device + // Thing parentThing = thingRepository.getThing(parentId); + // if (parentThing instanceof Bridge) { + // Bridge parentBridge = (Bridge) parentThing; + // Device device = thingRepository.createDevice(jObject); + // parentBridge.addDevice(device); + // Config.getInstance().writeConfigFile(); + // Notification.send(NotificationType.SUCCESS, "Device [" + device.id() + "] wurde erstellt."); + // break; + // } + // } + // } else if (mode.equals("delete")) { + // TODO implement deletion of things + // /* + // * Delete a Thing + // */ + // String thingId = JsonUtils.getAsString(jConfig, "thing"); + // thingRepository.removeThing(thingId); + // Config.getInstance().writeConfigFile(); + // Notification.send(NotificationType.SUCCESS, "Controller [" + thingId + "] wurde " + " gel�scht."); + // } else { + // throw new OpenemsException("Modus [" + mode + "] ist nicht implementiert."); + // } + // } + // TODO send new config after config update + // // Send new config + // JsonObject jMetadata = new JsonObject(); + // jMetadata.add("config", Config.getInstance().getMetaConfigJson()); + // JsonObject j = new JsonObject(); + // j.add("metadata", jMetadata); + // WebSocketUtils.send(this.websocket, j); + // } catch (OpenemsException | ClassNotFoundException e) { + // Notification.send(NotificationType.ERROR, e.getMessage()); + // } + // } + /** * Handle current data subscriptions * (try to keep synced with Backend.BrowserWebsocket) @@ -399,59 +454,9 @@ private synchronized void system(JsonObject jMessageId, JsonObject jSystem, Role + "] timeout [" + timeout + "]"); output = LinuxCommand.execute(password, command, background, timeout); } - // TODO use my own send() method everywhere in this class WebSocketUtils.send(this.websocket, DefaultMessages.systemExecuteReply(jMessageId, output)); } - // /* - // * Create new Thing - // */ - // JsonObject jObject = JsonUtils.getAsJsonObject(jConfig, "object"); - // String parentId = JsonUtils.getAsString(jConfig, "parent"); - // String thingId = JsonUtils.getAsString(jObject, "id"); - // if (thingId.startsWith("_")) { - // throw new ConfigException("IDs starting with underscore are reserved for internal use."); - // } - // if (thingRepository.getThingById(thingId).isPresent()) { - // throw new ConfigException("Thing Id is already existing."); - // } - // String clazzName = JsonUtils.getAsString(jObject, "class"); - // Class clazz = Class.forName(clazzName); - // if (Device.class.isAssignableFrom(clazz)) { - // // Device - // Thing parentThing = thingRepository.getThing(parentId); - // if (parentThing instanceof Bridge) { - // Bridge parentBridge = (Bridge) parentThing; - // Device device = thingRepository.createDevice(jObject); - // parentBridge.addDevice(device); - // Config.getInstance().writeConfigFile(); - // Notification.send(NotificationType.SUCCESS, "Device [" + device.id() + "] wurde erstellt."); - // break; - // } - // } - // } else if (mode.equals("delete")) { - // /* - // * Delete a Thing - // */ - // String thingId = JsonUtils.getAsString(jConfig, "thing"); - // thingRepository.removeThing(thingId); - // Config.getInstance().writeConfigFile(); - // Notification.send(NotificationType.SUCCESS, "Controller [" + thingId + "] wurde " + " gel�scht."); - // } else { - // throw new OpenemsException("Modus [" + mode + "] ist nicht implementiert."); - // } - // } - // // Send new config - // JsonObject jMetadata = new JsonObject(); - // // TODO jMetadata.add("config", Config.getInstance().getMetaConfigJson()); - // JsonObject j = new JsonObject(); - // j.add("metadata", jMetadata); - // WebSocketUtils.send(this.websocket, j); - // } catch (OpenemsException | ClassNotFoundException e) { - // Notification.send(NotificationType.ERROR, e.getMessage()); - // } - // } - /** * Send a message to the websocket. * diff --git a/edge/src/io/openems/core/utilities/websocket/Notification.java b/edge/src/io/openems/core/utilities/websocket/Notification.java deleted file mode 100644 index 1f3976b4ef6..00000000000 --- a/edge/src/io/openems/core/utilities/websocket/Notification.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.openems.core.utilities.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.websocket.NotificationType; - -public class Notification { - private Logger log = LoggerFactory.getLogger(Notification.class); - - private String message = ""; - private NotificationType type = NotificationType.INFO; - - public Notification(NotificationType type, String message) { - set(type, message); - } - - public void set(String message) { - this.message = message; - } - - public void set(NotificationType type, String message) { - this.type = type; - this.message = message; - } - - public void send() { - // log message to syslog - switch (type) { - case INFO: - case SUCCESS: - log.info(this.message); - break; - case ERROR: - log.error(this.message); - break; - case WARNING: - log.warn(this.message); - break; - case LOG: - // ignore - } - // TODO WebsocketServer.broadcastNotification(this); - } - - public static void send(NotificationType type, String message) { - Notification notification = new Notification(type, message); - notification.send(); - } - - public NotificationType getType() { - return type; - } - - public String getMessage() { - return message; - } -} diff --git a/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java b/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java index 46dfa6a8015..d46f60e4135 100644 --- a/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java +++ b/edge/src/io/openems/impl/controller/api/modbustcp/MyProcessImage.java @@ -72,41 +72,42 @@ public int getUnitID() { @Override public synchronized DigitalOut[] getDigitalOutRange(int offset, int count) throws IllegalAddressException { - // TODO + // TODO implement getDigitalOutRange this.throwIllegalAddressException("getDigitalOutRange is not implemented"); return new DigitalOut[] {}; } @Override public synchronized DigitalOut getDigitalOut(int ref) throws IllegalAddressException { + // TODO implement getDigitalOut log.warn("getDigitalOut is not implemented"); return null; } @Override public synchronized int getDigitalOutCount() { - // TODO + // TODO implement getDigitalOutCount log.warn("getDigitalOutCount is not implemented"); return 0; } @Override public synchronized DigitalIn[] getDigitalInRange(int offset, int count) throws IllegalAddressException { - // TODO + // TODO implement getDigitalInRange this.throwIllegalAddressException("getDigitalInRange is not implemented"); return new DigitalIn[] {}; } @Override public synchronized DigitalIn getDigitalIn(int ref) throws IllegalAddressException { - // TODO + // TODO implement getDigitalIn this.throwIllegalAddressException("getDigitalIn is not implemented"); return null; } @Override public synchronized int getDigitalInCount() { - // TODO + // TODO implement getDigitalInCount log.warn("getDigitalInCount is not implemented"); return 0; } @@ -138,14 +139,14 @@ public synchronized InputRegister[] getInputRegisterRange(int offset, int count) @Override public synchronized InputRegister getInputRegister(int ref) throws IllegalAddressException { - // TODO + // TODO implement getInputRegister this.throwIllegalAddressException("getInputRegister is not implemented"); return null; } @Override public synchronized int getInputRegisterCount() { - // TODO + // TODO implement getInputRegisterCount log.warn("getInputRegisterCount is not implemented"); return 0; } @@ -189,42 +190,49 @@ public synchronized Register getRegister(int ref) throws IllegalAddressException @Override public synchronized int getRegisterCount() { + // TODO implement getRegisterCount log.warn("getRegisterCount is not implemented"); return 0; } @Override public synchronized File getFile(int ref) throws IllegalAddressException { + // TODO implement getFile log.warn("getFile is not implemented"); return null; } @Override public synchronized File getFileByNumber(int ref) throws IllegalAddressException { + // TODO implement getFileByNumber log.warn("getFileByNumber is not implemented"); return null; } @Override public synchronized int getFileCount() { + // TODO implement getFileCount log.warn("getFileCount is not implemented"); return 0; } @Override public synchronized FIFO getFIFO(int ref) throws IllegalAddressException { + // TODO implement getFIFO log.warn("getFIFO is not implemented"); return null; } @Override public synchronized FIFO getFIFOByAddress(int ref) throws IllegalAddressException { + // TODO implement getFIFOByAddress log.warn("getFIFOByAddress is not implemented"); return null; } @Override public synchronized int getFIFOCount() { + // TODO implement getDigitalOutRange log.warn("getDigitalOutRange is not implemented"); return 0; } diff --git a/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java b/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java index ad52b867d9c..f172cf58e0e 100644 --- a/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java +++ b/edge/src/io/openems/impl/controller/api/modbustcp/MyRegister.java @@ -35,21 +35,21 @@ public int getRegisterNo() { @Override public int getValue() { - // TODO + // TODO implement getValue log.warn("getValue is not implemented"); return 0; } @Override public int toUnsignedShort() { - // TODO + // TODO implement toUnsignedShort log.warn("toUnsignedShort is not implemented"); return 0; } @Override public short toShort() { - // TODO + // TODO implement toShort log.warn("toShort is not implemented"); return 0; } diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java index f34f5c074f3..707e696b0de 100644 --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java @@ -28,7 +28,6 @@ import java.util.UUID; import org.java_websocket.WebSocket; -import org.java_websocket.framing.CloseFrame; import org.java_websocket.handshake.ClientHandshake; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,8 +105,8 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { log.info("User [" + user.getName() + "] logged in by token"); return; } catch (OpenemsException e) { - // TODO handle error - log.error(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */, + LogBehaviour.WRITE_TO_LOG, Notification.ERROR, e.getMessage()); } } } @@ -155,10 +154,9 @@ private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws /* * send authentication failed reply */ - JsonObject jReply = DefaultMessages.uiConnectionFailedReply(); + JsonObject jReply = DefaultMessages.uiLogoutReply(); WebSocketUtils.send(websocket, jReply); - websocket.closeConnection(CloseFrame.REFUSE, "Error while authenticating: " + e.getMessage()); - log.error(e.getMessage()); + log.info(e.getMessage()); return; } break; @@ -186,16 +184,19 @@ private void authenticate(JsonObject jAuthenticate, WebSocket websocket) throws if (h.getUserOpt().isPresent()) { User otherUser = h.getUserOpt().get(); if (otherUser.equals(thisUser)) { - // TODO send notification "user was logged out" + JsonObject jReply = DefaultMessages.uiLogoutReply(); + h.send(jReply); h.dispose(); } } } } - // TODO send notification + JsonObject jReply = DefaultMessages.uiLogoutReply(); + WebSocketUtils.send(websocket, jReply); } catch (OpenemsException e) { - log.error("Unable to close session [" + sessionToken + "]: " + e.getMessage()); - // TODO send notification + WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */, + LogBehaviour.WRITE_TO_LOG, Notification.ERROR, + "Unable to close session [" + sessionToken + "]: " + e.getMessage()); } } } @@ -213,8 +214,8 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { try { authenticate(jAuthenticateOpt.get(), websocket); } catch (OpenemsException e) { - // TODO error - log.error(e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */, + LogBehaviour.WRITE_TO_LOG, Notification.ERROR, e.getMessage()); } return; } @@ -224,15 +225,16 @@ protected void _onMessage(WebSocket websocket, JsonObject jMessage) { try { handler = getHandlerOrCloseWebsocket(websocket); } catch (OpenemsException e) { - log.error("onMessage Error: " + e.getMessage()); + WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */, + LogBehaviour.WRITE_TO_LOG, Notification.ERROR, "onMessage Error: " + e.getMessage()); return; } // get session Token from handler String token = handler.getSessionToken(); if (!this.sessionTokens.containsKey(token)) { - // TODO error: token is not anymore valid. - log.error("Token [" + token + "] is not anymore valid."); + WebSocketUtils.sendNotificationOrLogError(websocket, new JsonObject() /* empty message id */, + LogBehaviour.WRITE_TO_LOG, Notification.ERROR, "Token [" + token + "] is not anymore valid."); websocket.close(); return; } @@ -272,7 +274,7 @@ private void handleAuthenticationSuccessful(UiEdgeWebsocketHandler handler, User jEdges.add(jEdge); // send reply - JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply(handler.getSessionToken(), jEdges); + JsonObject jReply = DefaultMessages.uiLoginSuccessfulReply(handler.getSessionToken(), jEdges); handler.send(jReply); } @@ -305,7 +307,7 @@ private UiEdgeWebsocketHandler getHandlerOrCloseWebsocket(WebSocket websocket) t UiEdgeWebsocketHandler handler = this.handlers.get(uuid); if (!handlerOpt.isPresent()) { // no handler! close websocket - websocket.close(); // TODO error message + notification + websocket.close(); throw new OpenemsException("Websocket had no Handler. Closing websocket."); } return handler; diff --git a/edge/src/io/openems/impl/controller/asymmetric/fixvalue/FixValueReactivePowerController.java b/edge/src/io/openems/impl/controller/asymmetric/fixvalue/FixValueReactivePowerController.java index 86bbb185470..5220f81ca72 100644 --- a/edge/src/io/openems/impl/controller/asymmetric/fixvalue/FixValueReactivePowerController.java +++ b/edge/src/io/openems/impl/controller/asymmetric/fixvalue/FixValueReactivePowerController.java @@ -33,6 +33,7 @@ public class FixValueReactivePowerController extends Controller { private ThingStateChannels thingState = new ThingStateChannels(this); + /* * Constructors */ @@ -69,8 +70,7 @@ public void run() { ess.power.setReactivePower(reactivePowerL1.value(), reactivePowerL2.value(), reactivePowerL3.value()); } } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } diff --git a/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java b/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java index 586f0c3c5cf..bf6df1a612d 100644 --- a/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java +++ b/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java @@ -38,6 +38,7 @@ public ChargeLimitationController(String thingId) { */ @Override public void run() { + // TODO missing implementation for ChargeLimitationController // try { // Ess ess = this.ess.value(); // List chargers = this.chargers.value(); @@ -58,10 +59,9 @@ public void run() { // } // } // } catch (InvalidValueException e) { - // // TODO Auto-generated catch block + // // // e.printStackTrace(); // } catch (WriteChannelException e) { - // // TODO Auto-generated catch block // e.printStackTrace(); // } } diff --git a/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java b/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java index db556976f80..d1e8fb3389b 100644 --- a/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java +++ b/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java @@ -26,12 +26,8 @@ import io.openems.api.channel.thingstate.ThingStateChannels; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; -// TODO Access all relevant channels directly via ThingRepository - -@ThingInfo(title = "Output debugging information on systemlog") public class DebugLogController extends Controller { private ThingStateChannels thingState = new ThingStateChannels(this); diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java index 609b78fc825..bb8189934b2 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischarge/AvoidTotalDischargeController.java @@ -37,6 +37,7 @@ public class AvoidTotalDischargeController extends Controller { private ThingStateChannels thingState = new ThingStateChannels(this); + /* * Constructors */ @@ -84,8 +85,7 @@ public void run() { ess.setActivePower.pushWriteMax(-1000L); } } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } break; @@ -118,8 +118,7 @@ public void run() { try { ess.setActivePower.pushWriteMin(0L); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } if (ess.soc.value() < maxSoc.value()) { ess.currentState = State.NORMAL; @@ -131,7 +130,7 @@ public void run() { } } } catch (InvalidValueException e) { - log.error("no ess configured"+e.getMessage()); + log.error("no ess configured" + e.getMessage()); } } diff --git a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java index 1e4a8b614e4..c5a5b22fad3 100644 --- a/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java +++ b/edge/src/io/openems/impl/controller/symmetric/avoidtotaldischargesoctimeline/AvoidTotalDischargeSocTimeLineController.java @@ -89,8 +89,7 @@ public void run() { ess.setActivePower.pushWriteMax(-1000L); } } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } break; @@ -153,8 +152,7 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol ess.addTime(time, minSoc, chargeSoc); } } catch (InvalidValueException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); + log.error(e1.getMessage()); } } } diff --git a/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java b/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java index 66b48dbbab6..77f2b46b418 100644 --- a/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/refuavoidtotaldischarge/AvoidTotalDischargeController.java @@ -171,8 +171,7 @@ public void run() { ess.setActivePower.pushWriteMax(-1000L); } } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } }); @@ -184,19 +183,16 @@ public void run() { try { ess.setActivePower.pushWriteMin(0L); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } break; case ASC: try { ess.setActivePower.pushWriteMin((long) (ess.allowedCharge.value() * multiplier)); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } break; case BELOW: @@ -205,8 +201,7 @@ public void run() { try { ess.setActivePower.pushWriteMin(0L); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } break; default: diff --git a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java index 229647fabd3..f50310f5beb 100644 --- a/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java +++ b/edge/src/io/openems/impl/controller/symmetric/timelinecharge/TimelineChargeController.java @@ -216,8 +216,7 @@ public void run() { ess.setActivePower.pushWriteMax(-1000L); } } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } break; diff --git a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java index 131bf0e270a..e41872d9240 100644 --- a/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java +++ b/edge/src/io/openems/impl/controller/symmetric/voltagecharacteristic/VoltageCharacteristicController.java @@ -100,8 +100,7 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } pCharacteristic = points; } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } }); @@ -116,8 +115,7 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } qCharacteristic = points; } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } }); diff --git a/edge/src/io/openems/impl/controller/testwrite/TestWriteController.java b/edge/src/io/openems/impl/controller/testwrite/TestWriteController.java index 0bd38872623..a8cceef917f 100644 --- a/edge/src/io/openems/impl/controller/testwrite/TestWriteController.java +++ b/edge/src/io/openems/impl/controller/testwrite/TestWriteController.java @@ -50,11 +50,9 @@ public void run() { out.value().output1.pushWrite(true); log.info(in.value().input1.value().toString()); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } // for (Ess ess : esss) { // try { diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialCharger.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialCharger.java index 2a4badb246e..772be2cf101 100644 --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialCharger.java +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialCharger.java @@ -1315,7 +1315,6 @@ public ReadChannel getInputVoltage() { @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return thingState; } diff --git a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java index 9fe7fca9f18..cba5be9a5f1 100644 --- a/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java +++ b/edge/src/io/openems/impl/device/commercial/FeneconCommercialEss.java @@ -553,8 +553,7 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .faultBit(10,FaultEss.Phase1GridVoltageSamplingInvalidation)// .faultBit(11, FaultEss.Phase2VirtualCurrentOverLimitProtection)// .faultBit(12, FaultEss.Phase3VirtualCurrentOverLimitProtection)// - .faultBit(13, FaultEss.Phase1GridVoltageSamplingInvalidation2)// TODO same as - // above + .faultBit(13, FaultEss.Phase1GridVoltageSamplingInvalidation2)// .faultBit(14, FaultEss.Phase2ridVoltageSamplingInvalidation)// .faultBit(15, FaultEss.Phase3GridVoltageSamplingInvalidation)// ), // diff --git a/edge/src/io/openems/impl/device/keba/Keba.java b/edge/src/io/openems/impl/device/keba/Keba.java index 6558ec81a34..ce09636e8ae 100644 --- a/edge/src/io/openems/impl/device/keba/Keba.java +++ b/edge/src/io/openems/impl/device/keba/Keba.java @@ -36,7 +36,14 @@ * Example config: * *
        - * // TODO
        + *	{
        + *		"class": "io.openems.impl.controller.evcs.EvcsController",
        + *		"meter": "meter0",
        + *		"ess": "ess0",
        + *		"priority": 10,
        + *		"forceCharge": false,
        + *		"evcs": "evcs0"
        + *	}
          * 
        */ diff --git a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniConsumptionMeter.java b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniConsumptionMeter.java index 5b1994d8e0b..0395540ecf5 100644 --- a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniConsumptionMeter.java +++ b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniConsumptionMeter.java @@ -81,13 +81,11 @@ public ReadChannel apparentPower() { @Override public ReadChannel frequency() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel voltage() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniEss.java b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniEss.java index a617b2a37c3..97303fe28eb 100644 --- a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniEss.java +++ b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniEss.java @@ -197,13 +197,11 @@ public ReadChannel allowedApparent() { @Override public ReadChannel capacity() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel maxNominalPower() { - // TODO Auto-generated method stub return null; } @@ -224,43 +222,36 @@ public ReadChannel reactivePowerL3() { @Override public WriteChannel setActivePowerL1() { - // TODO Auto-generated method stub return null; } @Override public WriteChannel setActivePowerL2() { - // TODO Auto-generated method stub return null; } @Override public WriteChannel setActivePowerL3() { - // TODO Auto-generated method stub return null; } @Override public WriteChannel setReactivePowerL1() { - // TODO Auto-generated method stub return null; } @Override public WriteChannel setReactivePowerL2() { - // TODO Auto-generated method stub return null; } @Override public WriteChannel setReactivePowerL3() { - // TODO Auto-generated method stub return null; } @Override public WriteChannel setWorkState() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniGridMeter.java b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniGridMeter.java index f40ecf25b38..aefe8041dd9 100644 --- a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniGridMeter.java +++ b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniGridMeter.java @@ -82,13 +82,11 @@ public ReadChannel reactivePower() { @Override public ReadChannel frequency() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel voltage() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniProductionMeter.java b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniProductionMeter.java index eb310344a43..b33a5079cb2 100644 --- a/edge/src/io/openems/impl/device/minireadonly/FeneconMiniProductionMeter.java +++ b/edge/src/io/openems/impl/device/minireadonly/FeneconMiniProductionMeter.java @@ -81,13 +81,11 @@ public ReadChannel apparentPower() { @Override public ReadChannel frequency() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel voltage() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/pro/FeneconProEss.java b/edge/src/io/openems/impl/device/pro/FeneconProEss.java index 2e19bbd4743..76fff13d015 100644 --- a/edge/src/io/openems/impl/device/pro/FeneconProEss.java +++ b/edge/src/io/openems/impl/device/pro/FeneconProEss.java @@ -469,26 +469,37 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .warningBit(10, WarningEss.TmeErrorL1)), // Tme error new UnsignedWordElement(151, new ModbusBitWrappingChannel("PcsAlarm2L1", this, state)), new UnsignedWordElement(152, new ModbusBitWrappingChannel("PcsFault1L1", this, state)// - .faultBit(0, FaultEss.ControlCurrentOverload100PercentL1) // Control current overload 100% - .faultBit(1, FaultEss.ControlCurrentOverload110PercentL1) // Control current overload 110% - .faultBit(2, FaultEss.ControlCurrentOverload150PercentL1) // Control current overload 150% - .faultBit(3, FaultEss.ControlCurrentOverload200PercentL1) // Control current overload 200% - .faultBit(4, FaultEss.ControlCurrentOverload120PercentL1) // Control current overload 120% - .faultBit(5, FaultEss.ControlCurrentOverload300PercentL1) // Control current overload 300% + .faultBit(0, FaultEss.ControlCurrentOverload100PercentL1) // Control current overload + // 100% + .faultBit(1, FaultEss.ControlCurrentOverload110PercentL1) // Control current overload + // 110% + .faultBit(2, FaultEss.ControlCurrentOverload150PercentL1) // Control current overload + // 150% + .faultBit(3, FaultEss.ControlCurrentOverload200PercentL1) // Control current overload + // 200% + .faultBit(4, FaultEss.ControlCurrentOverload120PercentL1) // Control current overload + // 120% + .faultBit(5, FaultEss.ControlCurrentOverload300PercentL1) // Control current overload + // 300% .faultBit(6, FaultEss.ControlTransientLoad300PercentL1) // Control transient load 300% .faultBit(7, FaultEss.GridOverCurrentL1) // Grid over current .faultBit(8, FaultEss.LockingWaveformTooManyTimesL1) // Locking waveform too many times - .faultBit(9, FaultEss.InverterVoltageZeroDriftErrorL1) // Inverter voltage zero drift error + .faultBit(9, FaultEss.InverterVoltageZeroDriftErrorL1) // Inverter voltage zero drift + // error .faultBit(10, FaultEss.GridVoltageZeroDriftErrorL1) // Grid voltage zero drift error - .faultBit(11, FaultEss.ControlCurrentZeroDriftErrorL1) // Control current zero drift error - .faultBit(12, FaultEss.InverterCurrentZeroDriftErrorL1) // Inverter current zero drift error + .faultBit(11, FaultEss.ControlCurrentZeroDriftErrorL1) // Control current zero drift + // error + .faultBit(12, FaultEss.InverterCurrentZeroDriftErrorL1) // Inverter current zero drift + // error .faultBit(13, FaultEss.GridCurrentZeroDriftErrorL1) // Grid current zero drift error .faultBit(14, FaultEss.PDPProtectionL1) // PDP protection - .faultBit(15, FaultEss.HardwareControlCurrentProtectionL1)), // Hardware control current protection + .faultBit(15, FaultEss.HardwareControlCurrentProtectionL1)), // Hardware control current + // protection new UnsignedWordElement(153, new ModbusBitWrappingChannel("PcsFault2L1", this, state)// .faultBit(0, FaultEss.HardwareACVoltageProtectionL1) // Hardware AC volt. protection .faultBit(1, FaultEss.HardwareDCCurrentProtectionL1) // Hardware DC curr. protection - .faultBit(2, FaultEss.HardwareTemperatureProtectionL1) // Hardware temperature protection + .faultBit(2, FaultEss.HardwareTemperatureProtectionL1) // Hardware temperature + // protection .faultBit(3, FaultEss.NoCapturingSignalL1) // No capturing signal .faultBit(4, FaultEss.DCOvervoltageL1) // DC overvoltage .faultBit(5, FaultEss.DCDisconnectedL1) // DC disconnected @@ -514,8 +525,10 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .faultBit(8, FaultEss.LoadCurrentZeroDriftErrorL1) // Load current zero drift error .faultBit(9, FaultEss.CurrentLimitRErrorL1) // Current limit-R error .faultBit(10, FaultEss.PhaseSyncErrorL1) // Phase sync error - .faultBit(11, FaultEss.ExternalPVCurrentZeroDriftErrorL1) // External PV current zero drift error - .faultBit(12, FaultEss.ExternalGridCurrentZeroDriftErrorL1)), // External grid current zero drift error + .faultBit(11, FaultEss.ExternalPVCurrentZeroDriftErrorL1) // External PV current zero + // drift error + .faultBit(12, FaultEss.ExternalGridCurrentZeroDriftErrorL1)), // External grid current + // zero drift error new UnsignedWordElement(155, new ModbusBitWrappingChannel("PcsAlarm1L2", this, state)// .warningBit(0, WarningEss.GridUndervoltageL2) // Grid undervoltage .warningBit(1, WarningEss.GridOvervoltageL2) // Grid overvoltage @@ -530,26 +543,37 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .warningBit(10, WarningEss.TmeErrorL2)), // Tme error new UnsignedWordElement(156, new ModbusBitWrappingChannel("PcsAlarm2L2", this, state)), new UnsignedWordElement(157, new ModbusBitWrappingChannel("PcsFault1L2", this, state)// - .faultBit(0, FaultEss.ControlCurrentOverload100PercentL2) // Control current overload 100% - .faultBit(1, FaultEss.ControlCurrentOverload110PercentL2) // Control current overload 110% - .faultBit(2, FaultEss.ControlCurrentOverload150PercentL2) // Control current overload 150% - .faultBit(3, FaultEss.ControlCurrentOverload200PercentL2) // Control current overload 200% - .faultBit(4, FaultEss.ControlCurrentOverload120PercentL2) // Control current overload 120% - .faultBit(5, FaultEss.ControlCurrentOverload300PercentL2) // Control current overload 300% + .faultBit(0, FaultEss.ControlCurrentOverload100PercentL2) // Control current overload + // 100% + .faultBit(1, FaultEss.ControlCurrentOverload110PercentL2) // Control current overload + // 110% + .faultBit(2, FaultEss.ControlCurrentOverload150PercentL2) // Control current overload + // 150% + .faultBit(3, FaultEss.ControlCurrentOverload200PercentL2) // Control current overload + // 200% + .faultBit(4, FaultEss.ControlCurrentOverload120PercentL2) // Control current overload + // 120% + .faultBit(5, FaultEss.ControlCurrentOverload300PercentL2) // Control current overload + // 300% .faultBit(6, FaultEss.ControlTransientLoad300PercentL2) // Control transient load 300% .faultBit(7, FaultEss.GridOverCurrentL2) // Grid over current .faultBit(8, FaultEss.LockingWaveformTooManyTimesL2) // Locking waveform too many times - .faultBit(9, FaultEss.InverterVoltageZeroDriftErrorL2) // Inverter voltage zero drift error + .faultBit(9, FaultEss.InverterVoltageZeroDriftErrorL2) // Inverter voltage zero drift + // error .faultBit(10, FaultEss.GridVoltageZeroDriftErrorL2) // Grid voltage zero drift error - .faultBit(11, FaultEss.ControlCurrentZeroDriftErrorL2) // Control current zero drift error - .faultBit(12, FaultEss.InverterCurrentZeroDriftErrorL2) // Inverter current zero drift error + .faultBit(11, FaultEss.ControlCurrentZeroDriftErrorL2) // Control current zero drift + // error + .faultBit(12, FaultEss.InverterCurrentZeroDriftErrorL2) // Inverter current zero drift + // error .faultBit(13, FaultEss.GridCurrentZeroDriftErrorL2) // Grid current zero drift error .faultBit(14, FaultEss.PDPProtectionL2) // PDP protection - .faultBit(15, FaultEss.HardwareControlCurrentProtectionL2)), // Hardware control current protection + .faultBit(15, FaultEss.HardwareControlCurrentProtectionL2)), // Hardware control current + // protection new UnsignedWordElement(158, new ModbusBitWrappingChannel("PcsFault2L2", this, state)// .faultBit(0, FaultEss.HardwareACVoltageProtectionL2) // Hardware AC volt. protection .faultBit(1, FaultEss.HardwareDCCurrentProtectionL2) // Hardware DC curr. protection - .faultBit(2, FaultEss.HardwareTemperatureProtectionL2) // Hardware temperature protection + .faultBit(2, FaultEss.HardwareTemperatureProtectionL2) // Hardware temperature + // protection .faultBit(3, FaultEss.NoCapturingSignalL2) // No capturing signal .faultBit(4, FaultEss.DCOvervoltageL2) // DC overvoltage .faultBit(5, FaultEss.DCDisconnectedL2) // DC disconnected @@ -575,8 +599,10 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .faultBit(8, FaultEss.LoadCurrentZeroDriftErrorL2) // Load current zero drift error .faultBit(9, FaultEss.CurrentLimitRErrorL2) // Current limit-R error .faultBit(10, FaultEss.PhaseSyncErrorL2) // Phase sync error - .faultBit(11, FaultEss.ExternalPVCurrentZeroDriftErrorL2) // External PV current zero drift error - .faultBit(12, FaultEss.ExternalGridCurrentZeroDriftErrorL2)), // External grid current zero drift error + .faultBit(11, FaultEss.ExternalPVCurrentZeroDriftErrorL2) // External PV current zero + // drift error + .faultBit(12, FaultEss.ExternalGridCurrentZeroDriftErrorL2)), // External grid current + // zero drift error new UnsignedWordElement(160, new ModbusBitWrappingChannel("PcsAlarm1L3", this, state)// .warningBit(0, WarningEss.GridUndervoltageL3) // Grid undervoltage .warningBit(1, WarningEss.GridOvervoltageL3) // Grid overvoltage @@ -591,26 +617,37 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .warningBit(10, WarningEss.TmeErrorL3)), // Tme error new UnsignedWordElement(161, new ModbusBitWrappingChannel("PcsAlarm2L3", this, state)), new UnsignedWordElement(162, new ModbusBitWrappingChannel("PcsFault1L3", this, state)// - .faultBit(0, FaultEss.ControlCurrentOverload100PercentL3) // Control current overload 100% - .faultBit(1, FaultEss.ControlCurrentOverload110PercentL3) // Control current overload 110% - .faultBit(2, FaultEss.ControlCurrentOverload150PercentL3) // Control current overload 150% - .faultBit(3, FaultEss.ControlCurrentOverload200PercentL3) // Control current overload 200% - .faultBit(4, FaultEss.ControlCurrentOverload120PercentL3) // Control current overload 120% - .faultBit(5, FaultEss.ControlCurrentOverload300PercentL3) // Control current overload 300% + .faultBit(0, FaultEss.ControlCurrentOverload100PercentL3) // Control current overload + // 100% + .faultBit(1, FaultEss.ControlCurrentOverload110PercentL3) // Control current overload + // 110% + .faultBit(2, FaultEss.ControlCurrentOverload150PercentL3) // Control current overload + // 150% + .faultBit(3, FaultEss.ControlCurrentOverload200PercentL3) // Control current overload + // 200% + .faultBit(4, FaultEss.ControlCurrentOverload120PercentL3) // Control current overload + // 120% + .faultBit(5, FaultEss.ControlCurrentOverload300PercentL3) // Control current overload + // 300% .faultBit(6, FaultEss.ControlTransientLoad300PercentL3) // Control transient load 300% .faultBit(7, FaultEss.GridOverCurrentL3) // Grid over current .faultBit(8, FaultEss.LockingWaveformTooManyTimesL3) // Locking waveform too many times - .faultBit(9, FaultEss.InverterVoltageZeroDriftErrorL3) // Inverter voltage zero drift error + .faultBit(9, FaultEss.InverterVoltageZeroDriftErrorL3) // Inverter voltage zero drift + // error .faultBit(10, FaultEss.GridVoltageZeroDriftErrorL3) // Grid voltage zero drift error - .faultBit(11, FaultEss.ControlCurrentZeroDriftErrorL3) // Control current zero drift error - .faultBit(12, FaultEss.InverterCurrentZeroDriftErrorL3) // Inverter current zero drift error + .faultBit(11, FaultEss.ControlCurrentZeroDriftErrorL3) // Control current zero drift + // error + .faultBit(12, FaultEss.InverterCurrentZeroDriftErrorL3) // Inverter current zero drift + // error .faultBit(13, FaultEss.GridCurrentZeroDriftErrorL3) // Grid current zero drift error .faultBit(14, FaultEss.PDPProtectionL3) // PDP protection - .faultBit(15, FaultEss.HardwareControlCurrentProtectionL3)), // Hardware control current protection + .faultBit(15, FaultEss.HardwareControlCurrentProtectionL3)), // Hardware control current + // protection new UnsignedWordElement(163, new ModbusBitWrappingChannel("PcsFault2L3", this, state)// .faultBit(0, FaultEss.HardwareACVoltageProtectionL3) // Hardware AC volt. protection .faultBit(1, FaultEss.HardwareDCCurrentProtectionL3) // Hardware DC curr. protection - .faultBit(2, FaultEss.HardwareTemperatureProtectionL3) // Hardware temperature protection + .faultBit(2, FaultEss.HardwareTemperatureProtectionL3) // Hardware temperature + // protection .faultBit(3, FaultEss.NoCapturingSignalL3) // No capturing signal .faultBit(4, FaultEss.DCOvervoltageL3) // DC overvoltage .faultBit(5, FaultEss.DCDisconnectedL3) // DC disconnected @@ -636,8 +673,10 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { .faultBit(8, FaultEss.LoadCurrentZeroDriftErrorL3) // Load current zero drift error .faultBit(9, FaultEss.CurrentLimitRErrorL3) // Current limit-R error .faultBit(10, FaultEss.PhaseSyncErrorL3) // Phase sync error - .faultBit(11, FaultEss.ExternalPVCurrentZeroDriftErrorL3) // External PV current zero drift error - .faultBit(12, FaultEss.ExternalGridCurrentZeroDriftErrorL3))), // External grid current zero drift error + .faultBit(11, FaultEss.ExternalPVCurrentZeroDriftErrorL3) // External PV current zero + // drift error + .faultBit(12, FaultEss.ExternalGridCurrentZeroDriftErrorL3))), // External grid current + // zero drift error new WriteableModbusRegisterRange(200, // new UnsignedWordElement(200, setWorkState = new ModbusWriteLongChannel("SetWorkState", this)// .label(0, "Local control") // @@ -805,8 +844,7 @@ protected ModbusProtocol defineModbusProtocol() throws ConfigException { try { return apparent.value() * 3; } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } return 0l; }, phaseAllowedApparent); diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorGridMeter.java b/edge/src/io/openems/impl/device/simulator/SimulatorGridMeter.java index 236ade71d59..6d9d7297274 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorGridMeter.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorGridMeter.java @@ -136,30 +136,14 @@ private LoadGenerator getGenerator(JsonObject config) { try { Constructor constructor = clazz.getConstructor(JsonObject.class); return (LoadGenerator) constructor.newInstance(config.get("config").getAsJsonObject()); - } catch (NoSuchMethodException e) { + } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) { } } return (LoadGenerator) clazz.newInstance(); - } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (SecurityException | InstantiationException | IllegalAccessException | ClassNotFoundException e) { + log.error(e.getMessage()); } return null; } @@ -341,37 +325,31 @@ public ReadChannel reactivePowerL3() { @Override public ReadChannel currentL1() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel currentL2() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel currentL3() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel voltageL1() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel voltageL2() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel voltageL3() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorProductionMeter.java b/edge/src/io/openems/impl/device/simulator/SimulatorProductionMeter.java index 350cef2a883..18ba474c983 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorProductionMeter.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorProductionMeter.java @@ -100,30 +100,14 @@ private LoadGenerator getGenerator(JsonObject config) { try { Constructor constructor = clazz.getConstructor(JsonObject.class); return (LoadGenerator) constructor.newInstance(config.get("config").getAsJsonObject()); - } catch (NoSuchMethodException e) { - + } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) { + log.error(e.getMessage()); } } return (LoadGenerator) clazz.newInstance(); - } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (SecurityException | InstantiationException | IllegalAccessException | ClassNotFoundException e) { + log.error(e.getMessage()); } return null; } diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index 37bf03422cc..076a5c23a14 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -115,8 +115,7 @@ public SimulatorSymmetricEss(String thingId, Device parent) throws ConfigExcepti } return (long) (energy / capacity.value() * 100.0); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } return 0L; }, this.activePower); @@ -299,30 +298,14 @@ private LoadGenerator getGenerator(JsonObject config) { try { Constructor constructor = clazz.getConstructor(JsonObject.class); return (LoadGenerator) constructor.newInstance(config.get("config").getAsJsonObject()); - } catch (NoSuchMethodException e) { + } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) { } } return (LoadGenerator) clazz.newInstance(); - } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (SecurityException | InstantiationException | IllegalAccessException | ClassNotFoundException e) { + log.error(e.getMessage()); } return null; } diff --git a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java index 1b3d04ee38f..167f07dfbd1 100644 --- a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java +++ b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss1.java @@ -272,13 +272,11 @@ public ReadChannel activePower() { @Override public ReadChannel apparentPower() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel reactivePower() { - // TODO Auto-generated method stub return null; } @@ -289,7 +287,6 @@ public WriteChannel setActivePower() { @Override public WriteChannel setReactivePower() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java index 70132ed2515..e7681ef1b18 100644 --- a/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java +++ b/edge/src/io/openems/impl/device/streetscooter/StreetscooterEss2.java @@ -273,13 +273,11 @@ public ReadChannel activePower() { @Override public ReadChannel apparentPower() { - // TODO Auto-generated method stub return null; } @Override public ReadChannel reactivePower() { - // TODO Auto-generated method stub return null; } @@ -290,7 +288,6 @@ public WriteChannel setActivePower() { @Override public WriteChannel setReactivePower() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/impl/device/system/SystemNature.java b/edge/src/io/openems/impl/device/system/SystemNature.java index 029145e4386..5a2b2f48bda 100644 --- a/edge/src/io/openems/impl/device/system/SystemNature.java +++ b/edge/src/io/openems/impl/device/system/SystemNature.java @@ -74,9 +74,9 @@ public ReadChannel primaryIpAddress() { return primaryIpAddress; } + // TODO get real version private StaticValueChannel openemsVersionMajor = new StaticValueChannel("OpenemsVersionMajor", this, 1); - // TODO https://stackoverflow.com/questions/2712970/get-maven-artifact-version-at-runtime @Override public ReadChannel openemsVersionMajor() { diff --git a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java index 7d29fb414a5..d7e62a7faa8 100644 --- a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java +++ b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEss.java @@ -19,7 +19,6 @@ public class AsymmetricSymmetricCombinationEss extends SystemDevice { public AsymmetricSymmetricCombinationEss(Bridge parent) throws OpenemsException { super(parent); - // TODO Auto-generated constructor stub } @Override diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java b/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java index 6d8022bf976..d1b1bec8b69 100644 --- a/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java +++ b/edge/src/io/openems/impl/device/system/esscluster/EssCluster.java @@ -19,7 +19,6 @@ public class EssCluster extends SystemDevice { public EssCluster(Bridge parent) throws OpenemsException { super(parent); - // TODO Auto-generated constructor stub } @Override diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java index 2492c0e5ce2..ddd7cb4e2e7 100644 --- a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java +++ b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java @@ -662,8 +662,7 @@ protected void update() { loadEss(); } } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error(e.getMessage()); } } diff --git a/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java b/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java index 7c496bd2e8c..5b330f499f7 100644 --- a/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java +++ b/edge/src/io/openems/impl/device/system/metercluster/MeterCluster.java @@ -19,7 +19,6 @@ public class MeterCluster extends SystemDevice { public MeterCluster(Bridge parent) throws OpenemsException { super(parent); - // TODO Auto-generated constructor stub } @Override diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index 6d7a518ec9e..9a3c28c7e04 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -112,7 +112,7 @@ public FeneconPersistence() { try { WebSocketUtils.send( // websocket, // - DefaultMessages.configQueryReply(new JsonObject() /* TODO */, Config.getInstance() + DefaultMessages.configQueryReply(new JsonObject(), Config.getInstance() .getJson(ConfigFormat.OPENEMS_UI, Role.ADMIN, DEFAULT_CONFIG_LANGUAGE))); log.info("Sent config to FENECON persistence."); } catch (OpenemsException e) { diff --git a/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java b/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java index b332afc0dd3..02025f1e0c5 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java +++ b/edge/src/io/openems/impl/persistence/fenecon/ReconnectingWebsocket.java @@ -88,7 +88,6 @@ public void onMessage(String message) { } catch (OutOfMemoryError e) { // Java-Websocket library can cause an "unable to create new native thread" OutOfMemoryError on // subscribe. We are not able to recover that. - // TODO fix this bug App.shutdownWithError("ReconnectingWebsocket. Error on message [" + message + "]", e); } catch (Throwable t) { log.error("Websocket [" + this.getURI().toString() + "] error on message [" + message + "]: " @@ -209,7 +208,7 @@ public void setUri(Optional uriOpt, Optional proxyOpt) { * @param value */ public void addHttpHeader(String key, String value) { - // TODO this is not able to handle changes after websocket was established + // this is not able to handle changes after websocket was established this.httpHeaders.put(key, value); } diff --git a/edge/src/io/openems/impl/persistence/influxdb/FieldValue.java b/edge/src/io/openems/impl/persistence/influxdb/FieldValue.java index cc1fd6bb987..b70d1a52bc1 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/FieldValue.java +++ b/edge/src/io/openems/impl/persistence/influxdb/FieldValue.java @@ -1,32 +1,31 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.persistence.influxdb; - -//TODO move to common package -public abstract class FieldValue { - public final String field; - public final T value; - - public FieldValue(String field, T value) { - this.field = field; - this.value = value; - } -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.persistence.influxdb; + +public abstract class FieldValue { + public final String field; + public final T value; + + public FieldValue(String field, T value) { + this.field = field; + this.value = value; + } +} diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java index 9ef151fa433..17e4cde93a3 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbPersistence.java @@ -45,6 +45,7 @@ import io.openems.api.persistence.QueryablePersistence; import io.openems.backend.timedata.influx.InfluxdbUtils; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; import io.openems.common.types.ChannelEnum; import io.openems.core.Databus; @@ -227,4 +228,15 @@ protected int getCycleTime() { public ThingStateChannels getStateChannel() { return this.thingState; } + + @Override + public Optional getChannelValue(int edgeId, ChannelAddress channelAddress) { + log.error("getChannelValue is not implemented"); + return Optional.empty(); + } + + @Override + public void write(int edgeId, JsonObject jData) throws OpenemsException { + throw new OpenemsException("write is not implemented"); + } } \ No newline at end of file diff --git a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java index d2840baedba..ca44712cfc7 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java +++ b/edge/src/io/openems/impl/persistence/influxdb/InfluxdbQueryWrapper.java @@ -51,8 +51,6 @@ public static JsonObject query(Optional _influxdb, Optional f if (_influxdb.isPresent()) { InfluxDB influxdb = _influxdb.get(); jData = InfluxdbQueryWrapper.queryData(influxdb, fems, fromDate, toDate, channels, resolution, dbName); - // TODO jkWh = InfluxdbQueryWrapper.querykWh(influxdb, fems, fromDate, toDate, channels, resolution, kWh, - // dbName); } else { jData = new JsonArray(); jkWh = new JsonObject(); @@ -137,7 +135,7 @@ private static JsonArray queryData(InfluxDB influxdb, Optional fems, Zo return j; } - // TODO + // TODO implement query for kWh values // private static JsonObject querykWh(InfluxDB influxdb, Optional fems, ZonedDateTime fromDate, // ZonedDateTime toDate, JsonObject channels, int resolution, JsonObject kWh, String dbName) // throws OpenemsException { diff --git a/edge/src/io/openems/impl/persistence/influxdb/NumberFieldValue.java b/edge/src/io/openems/impl/persistence/influxdb/NumberFieldValue.java index 59bb8b46d9c..50ef53a0565 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/NumberFieldValue.java +++ b/edge/src/io/openems/impl/persistence/influxdb/NumberFieldValue.java @@ -1,30 +1,29 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.persistence.influxdb; - -//TODO move to common package -public class NumberFieldValue extends FieldValue { - - public NumberFieldValue(String field, Number value) { - super(field, value); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.persistence.influxdb; + +public class NumberFieldValue extends FieldValue { + + public NumberFieldValue(String field, Number value) { + super(field, value); + } + +} diff --git a/edge/src/io/openems/impl/persistence/influxdb/StringFieldValue.java b/edge/src/io/openems/impl/persistence/influxdb/StringFieldValue.java index 72ce5e8b202..77989efc20c 100644 --- a/edge/src/io/openems/impl/persistence/influxdb/StringFieldValue.java +++ b/edge/src/io/openems/impl/persistence/influxdb/StringFieldValue.java @@ -1,30 +1,29 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.persistence.influxdb; - -// TODO move to common package -public class StringFieldValue extends FieldValue { - - public StringFieldValue(String field, String value) { - super(field, value); - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.persistence.influxdb; + +public class StringFieldValue extends FieldValue { + + public StringFieldValue(String field, String value) { + super(field, value); + } + +} diff --git a/edge/src/io/openems/impl/protocol/modbus/ModbusCoilWriteChannel.java b/edge/src/io/openems/impl/protocol/modbus/ModbusCoilWriteChannel.java index 77c80401058..e887285b0de 100644 --- a/edge/src/io/openems/impl/protocol/modbus/ModbusCoilWriteChannel.java +++ b/edge/src/io/openems/impl/protocol/modbus/ModbusCoilWriteChannel.java @@ -1,40 +1,39 @@ -/******************************************************************************* - * 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.protocol.modbus; - -import io.openems.api.thing.Thing; - -public class ModbusCoilWriteChannel extends ModbusWriteChannel { - - public ModbusCoilWriteChannel(String id, Thing parent) { - super(id, parent); - // TODO Auto-generated Constructors stub - } - - @Override public ModbusCoilWriteChannel required() { - super.required(); - return this; - } - - @Override protected void updateValue(Boolean value) { - super.updateValue(value); - } -} +/******************************************************************************* + * 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.protocol.modbus; + +import io.openems.api.thing.Thing; + +public class ModbusCoilWriteChannel extends ModbusWriteChannel { + + public ModbusCoilWriteChannel(String id, Thing parent) { + super(id, parent); + } + + @Override public ModbusCoilWriteChannel required() { + super.required(); + return this; + } + + @Override protected void updateValue(Boolean value) { + super.updateValue(value); + } +} diff --git a/edge/src/io/openems/impl/protocol/simulator/SimulatorWriteChannel.java b/edge/src/io/openems/impl/protocol/simulator/SimulatorWriteChannel.java index ef99b97608d..1c5acd67071 100644 --- a/edge/src/io/openems/impl/protocol/simulator/SimulatorWriteChannel.java +++ b/edge/src/io/openems/impl/protocol/simulator/SimulatorWriteChannel.java @@ -1,28 +1,27 @@ -package io.openems.impl.protocol.simulator; - -import io.openems.api.channel.WriteChannel; -import io.openems.api.thing.Thing; - -public class SimulatorWriteChannel extends WriteChannel { - - public SimulatorWriteChannel(String id, Thing parent, T initialValue) { - super(id, parent); - updateValue(initialValue); - } - - @Override - public synchronized void shadowCopyAndReset() { - super.shadowCopyAndReset(); - if (writeShadowCopy.isPresent()) { - super.updateValue(writeShadowCopy.get()); - } - } - - @Override - public SimulatorWriteChannel label(T value, String label) { - // TODO Auto-generated method stub - super.label(value, label); - return this; - } - -} +package io.openems.impl.protocol.simulator; + +import io.openems.api.channel.WriteChannel; +import io.openems.api.thing.Thing; + +public class SimulatorWriteChannel extends WriteChannel { + + public SimulatorWriteChannel(String id, Thing parent, T initialValue) { + super(id, parent); + updateValue(initialValue); + } + + @Override + public synchronized void shadowCopyAndReset() { + super.shadowCopyAndReset(); + if (writeShadowCopy.isPresent()) { + super.updateValue(writeShadowCopy.get()); + } + } + + @Override + public SimulatorWriteChannel label(T value, String label) { + super.label(value, label); + return this; + } + +} diff --git a/edge/src/io/openems/impl/protocol/studer/internal/StuderConnection.java b/edge/src/io/openems/impl/protocol/studer/internal/StuderConnection.java index b9881f873c2..97007ffcf2f 100644 --- a/edge/src/io/openems/impl/protocol/studer/internal/StuderConnection.java +++ b/edge/src/io/openems/impl/protocol/studer/internal/StuderConnection.java @@ -1,148 +1,150 @@ -package io.openems.impl.protocol.studer.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.fazecast.jSerialComm.SerialPort; - -import io.openems.impl.protocol.studer.internal.object.ObjectType; -import io.openems.impl.protocol.studer.internal.object.PropertyId; - -/** - * Holds a serial connection to a Studer device - * - * @author stefan.feilmeier - */ -public class StuderConnection { - - private SerialPort connection = null; - private Request request = null; - - public StuderConnection(String portName) { - connection = SerialPort.getCommPort(portName); - connection.setComPortParameters(38400, 8, SerialPort.ONE_STOP_BIT, SerialPort.EVEN_PARITY); - connection.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0); - } - - public void connect() { - if (this.connection != null) { - connection.openPort(); - } - } - - public boolean isConnected() { - return connection.isOpen(); - } - - public void disconnect() { - if (this.connection != null) { - this.connection.closePort(); - } - } - - public void setRequest(Request request) { - this.request = request; - } - - public void execute() throws IOException { - if (connection != null && connection.isOpen() && request != null) { - OutputStream out = connection.getOutputStream(); - for (byte b : request.getBytes()) { - out.write(b); - } - out.flush(); - out.close(); - InputStream in = connection.getInputStream(); - byte startByte = (byte) in.read(); - if (startByte == (byte) 0xAA) { - List checksumBytes = new ArrayList<>(); - byte frameTags = (byte) in.read(); - boolean isMessagePending = (frameTags & 1) != 0; - boolean isRccRestart = (frameTags & 2) != 0; - boolean isSdCardPresent = (frameTags & 4) != 0; - boolean isSdCardFull = (frameTags & 8) != 0; - boolean isNewDataloggerFilePresent = (frameTags & 16) != 0; - boolean isDatalogSupported = (frameTags & 32) != 0; - checksumBytes.add(frameTags); - byte[] srcAddressBytes = new byte[4]; - for (int i = 0; i < 4; i++) { - byte b = (byte) in.read(); - srcAddressBytes[i] = b; - checksumBytes.add(b); - } - int srcAddress = ByteBuffer.wrap(srcAddressBytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); - byte[] dstAddressBytes = new byte[4]; - for (int i = 0; i < 4; i++) { - byte b = (byte) in.read(); - dstAddressBytes[i] = b; - checksumBytes.add(b); - } - int dstAddress = ByteBuffer.wrap(dstAddressBytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); - byte[] dataLengthBytes = new byte[2]; - for (int i = 0; i < 2; i++) { - byte b = (byte) in.read(); - dataLengthBytes[i] = b; - checksumBytes.add(b); - } - short dataLength = ByteBuffer.wrap(dataLengthBytes).order(ByteOrder.LITTLE_ENDIAN).getShort(); - byte[] headerChecksumBytes = new byte[2]; - for (int i = 0; i < 2; i++) { - byte b = (byte) in.read(); - headerChecksumBytes[i] = b; - } - List calculatedHeaderChecksum = Service.calculateChecksum(checksumBytes); - if (headerChecksumBytes[0] == calculatedHeaderChecksum.get(0) - && headerChecksumBytes[1] == calculatedHeaderChecksum.get(1)) { - byte[] dataBytes = new byte[dataLength]; - checksumBytes.clear(); - for (int i = 0; i < dataLength; i++) { - byte b = (byte) in.read(); - dataBytes[i] = b; - checksumBytes.add(b); - } - byte[] dataChecksumBytes = new byte[2]; - dataChecksumBytes[0] = (byte) in.read(); - dataChecksumBytes[1] = (byte) in.read(); - List calculatedDataChecksum = Service.calculateChecksum(checksumBytes); - if (dataChecksumBytes[0] == calculatedDataChecksum.get(0) - && dataChecksumBytes[1] == calculatedDataChecksum.get(1)) { - ByteBuffer buffer = ByteBuffer.wrap(dataBytes).order(ByteOrder.LITTLE_ENDIAN); - byte dataFlagsByte = dataBytes[0]; - boolean isError = (dataFlagsByte & 1) != 0; - boolean isResponse = (dataFlagsByte & 2) != 0; - byte serviceIdByte = dataBytes[1]; - ObjectType objectType = ObjectType.getByCode(buffer.getShort(2)); - int objectId = buffer.getInt(4); - PropertyId propertyId = PropertyId.getByCode(buffer.getShort(8)); - if (this.request.getServiceId() != serviceIdByte) { - System.out.println("ServiceId of Response is not equals Request ServiceId."); - } else { - this.request.createResponse(isResponse, isError, isDatalogSupported, - isNewDataloggerFilePresent, isSdCardFull, isSdCardPresent, isRccRestart, - isMessagePending, srcAddress, dstAddress, objectType, propertyId, objectId, - Arrays.copyOfRange(dataBytes, 10, dataBytes.length)); - } - } else { - // TODO Exception DataChecksum wrong - System.out.println("DataChecksum wrong"); - } - } else { - // TODO Exception HeaderChecksum wrong - System.out.println("HeaderChecksum wrong"); - } - } else { - // TODO Exception Stream start not found - System.out.println("Stream start not found."); - } - in.close(); - } else { - throw new IOException("Connection is not open!"); - } - } -} +package io.openems.impl.protocol.studer.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.fazecast.jSerialComm.SerialPort; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.impl.protocol.studer.internal.object.ObjectType; +import io.openems.impl.protocol.studer.internal.object.PropertyId; + +/** + * Holds a serial connection to a Studer device + * + * @author stefan.feilmeier + */ +public class StuderConnection { + + private SerialPort connection = null; + private Request request = null; + + public StuderConnection(String portName) { + connection = SerialPort.getCommPort(portName); + connection.setComPortParameters(38400, 8, SerialPort.ONE_STOP_BIT, SerialPort.EVEN_PARITY); + connection.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0); + } + + public void connect() { + if (this.connection != null) { + connection.openPort(); + } + } + + public boolean isConnected() { + return connection.isOpen(); + } + + public void disconnect() { + if (this.connection != null) { + this.connection.closePort(); + } + } + + public void setRequest(Request request) { + this.request = request; + } + + public void execute() throws OpenemsException { + try { + if (connection != null && connection.isOpen() && request != null) { + OutputStream out = connection.getOutputStream(); + for (byte b : request.getBytes()) { + out.write(b); + } + out.flush(); + out.close(); + InputStream in = connection.getInputStream(); + byte startByte = (byte) in.read(); + if (startByte == (byte) 0xAA) { + List checksumBytes = new ArrayList<>(); + byte frameTags = (byte) in.read(); + boolean isMessagePending = (frameTags & 1) != 0; + boolean isRccRestart = (frameTags & 2) != 0; + boolean isSdCardPresent = (frameTags & 4) != 0; + boolean isSdCardFull = (frameTags & 8) != 0; + boolean isNewDataloggerFilePresent = (frameTags & 16) != 0; + boolean isDatalogSupported = (frameTags & 32) != 0; + checksumBytes.add(frameTags); + byte[] srcAddressBytes = new byte[4]; + for (int i = 0; i < 4; i++) { + byte b = (byte) in.read(); + srcAddressBytes[i] = b; + checksumBytes.add(b); + } + int srcAddress = ByteBuffer.wrap(srcAddressBytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); + byte[] dstAddressBytes = new byte[4]; + for (int i = 0; i < 4; i++) { + byte b = (byte) in.read(); + dstAddressBytes[i] = b; + checksumBytes.add(b); + } + int dstAddress = ByteBuffer.wrap(dstAddressBytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); + byte[] dataLengthBytes = new byte[2]; + for (int i = 0; i < 2; i++) { + byte b = (byte) in.read(); + dataLengthBytes[i] = b; + checksumBytes.add(b); + } + short dataLength = ByteBuffer.wrap(dataLengthBytes).order(ByteOrder.LITTLE_ENDIAN).getShort(); + byte[] headerChecksumBytes = new byte[2]; + for (int i = 0; i < 2; i++) { + byte b = (byte) in.read(); + headerChecksumBytes[i] = b; + } + List calculatedHeaderChecksum = Service.calculateChecksum(checksumBytes); + if (headerChecksumBytes[0] == calculatedHeaderChecksum.get(0) + && headerChecksumBytes[1] == calculatedHeaderChecksum.get(1)) { + byte[] dataBytes = new byte[dataLength]; + checksumBytes.clear(); + for (int i = 0; i < dataLength; i++) { + byte b = (byte) in.read(); + dataBytes[i] = b; + checksumBytes.add(b); + } + byte[] dataChecksumBytes = new byte[2]; + dataChecksumBytes[0] = (byte) in.read(); + dataChecksumBytes[1] = (byte) in.read(); + List calculatedDataChecksum = Service.calculateChecksum(checksumBytes); + if (dataChecksumBytes[0] == calculatedDataChecksum.get(0) + && dataChecksumBytes[1] == calculatedDataChecksum.get(1)) { + ByteBuffer buffer = ByteBuffer.wrap(dataBytes).order(ByteOrder.LITTLE_ENDIAN); + byte dataFlagsByte = dataBytes[0]; + boolean isError = (dataFlagsByte & 1) != 0; + boolean isResponse = (dataFlagsByte & 2) != 0; + byte serviceIdByte = dataBytes[1]; + ObjectType objectType = ObjectType.getByCode(buffer.getShort(2)); + int objectId = buffer.getInt(4); + PropertyId propertyId = PropertyId.getByCode(buffer.getShort(8)); + if (this.request.getServiceId() != serviceIdByte) { + System.out.println("ServiceId of Response is not equals Request ServiceId."); + } else { + this.request.createResponse(isResponse, isError, isDatalogSupported, + isNewDataloggerFilePresent, isSdCardFull, isSdCardPresent, isRccRestart, + isMessagePending, srcAddress, dstAddress, objectType, propertyId, objectId, + Arrays.copyOfRange(dataBytes, 10, dataBytes.length)); + } + } else { + throw new OpenemsException("DataChecksum wrong"); + } + } else { + throw new OpenemsException("HeaderChecksum wrong"); + } + } else { + throw new OpenemsException("Stream start not found."); + } + in.close(); + } else { + throw new OpenemsException("Connection is not open!"); + } + } catch (IOException e) { + throw new OpenemsException("IOException: " + e.getMessage()); + } + } +} diff --git a/edge/src/io/openems/impl/protocol/system/SystemDeviceNature.java b/edge/src/io/openems/impl/protocol/system/SystemDeviceNature.java index cc5290a62d9..917cc45a5d8 100644 --- a/edge/src/io/openems/impl/protocol/system/SystemDeviceNature.java +++ b/edge/src/io/openems/impl/protocol/system/SystemDeviceNature.java @@ -87,7 +87,6 @@ public Device getParent() { @Override public List getReadTasks() { - // TODO Auto-generated method stub return null; } @@ -98,7 +97,6 @@ public List getRequiredReadTasks() { @Override public List getWriteTasks() { - // TODO Auto-generated method stub return null; } } diff --git a/edge/src/io/openems/test/controller/ThermalPowerStationTest.java b/edge/src/io/openems/test/controller/ThermalPowerStationTest.java index 106b3079b18..c8c3fe7e40e 100644 --- a/edge/src/io/openems/test/controller/ThermalPowerStationTest.java +++ b/edge/src/io/openems/test/controller/ThermalPowerStationTest.java @@ -12,6 +12,7 @@ import io.openems.api.channel.thingstate.ThingStateChannels; import io.openems.api.thing.Thing; +import io.openems.common.utils.Log; import io.openems.impl.controller.thermalpowerstation.Ess; import io.openems.impl.controller.thermalpowerstation.Meter; import io.openems.impl.controller.thermalpowerstation.ThermalPowerStationController; @@ -42,7 +43,6 @@ public String id() { @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } }); @@ -98,7 +98,6 @@ public void test2() { try { Thread.sleep(1000); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } // OFF @@ -124,8 +123,7 @@ public void test3() { try { Thread.sleep(1000); } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } controller.run(); outputChannel.shadowCopyAndReset(); diff --git a/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java b/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java index 38db17dd3af..e354de28aef 100644 --- a/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java +++ b/edge/src/io/openems/test/controller/supplybusswitch/SupplyBusTest.java @@ -13,6 +13,7 @@ import io.openems.api.channel.WriteChannel; import io.openems.api.exception.InvalidValueException; +import io.openems.common.utils.Log; import io.openems.impl.controller.supplybusswitch.Ess; import io.openems.impl.controller.supplybusswitch.Supplybus; import io.openems.test.utils.channel.UnitTestWriteChannel; @@ -120,8 +121,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } output1.shadowCopyAndReset(); output2.shadowCopyAndReset(); @@ -132,8 +132,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } output1.shadowCopyAndReset(); output2.shadowCopyAndReset(); @@ -144,8 +143,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } assertEquals(sbOnIndication.getWriteValue().isPresent(), true); assertEquals((long) sbOnIndication.getWriteValue().get(), 0); @@ -158,8 +156,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } // output not set switchdelay not expired assertEquals(output3.getWriteValue().isPresent(), false); @@ -171,9 +168,8 @@ public void test1() { // Sleep until switchdelay expired try { Thread.sleep(1000L); - } catch (InterruptedException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); + } catch (InterruptedException e) { + Log.error(e.getMessage()); } output1.shadowCopyAndReset(); output2.shadowCopyAndReset(); @@ -184,8 +180,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } assertEquals(output3.getWriteValue().isPresent(), true); assertEquals(output3.getWriteValue().get(), true); @@ -199,8 +194,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } output1.shadowCopyAndReset(); output2.shadowCopyAndReset(); @@ -211,8 +205,7 @@ public void test1() { try { sb.run(); } catch (InvalidValueException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } assertEquals(sbOnIndication.getWriteValue().isPresent(), true); assertEquals((long) sbOnIndication.getWriteValue().get(), 1L); diff --git a/edge/src/io/openems/test/core/utils/AsymmetricPowerTest.java b/edge/src/io/openems/test/core/utils/AsymmetricPowerTest.java index 9e50b1c17c2..9fd291d6a0b 100644 --- a/edge/src/io/openems/test/core/utils/AsymmetricPowerTest.java +++ b/edge/src/io/openems/test/core/utils/AsymmetricPowerTest.java @@ -9,6 +9,7 @@ import org.junit.runners.MethodSorters; import io.openems.api.exception.WriteChannelException; +import io.openems.common.utils.Log; import io.openems.core.utilities.AsymmetricPower; import io.openems.core.utilities.AsymmetricPower.ReductionType; import io.openems.test.utils.devicenatures.UnitTestAsymmetricEssNature; @@ -47,8 +48,7 @@ public void test1() { try { power.writePower(ReductionType.PERPHASE); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } long activePowerL1 = ess.setActivePowerL1.peekWrite().get(); long activePowerL2 = ess.setActivePowerL2.peekWrite().get(); @@ -74,8 +74,7 @@ public void test2() { try { power.writePower(ReductionType.PERPHASE); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } long activePowerL1 = ess.setActivePowerL1.peekWrite().get(); long activePowerL2 = ess.setActivePowerL2.peekWrite().get(); @@ -100,15 +99,13 @@ public void test3() { try { power.writePower(ReductionType.PERPHASE); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } power.setReactivePower(500, -900, 700); try { power.writePower(ReductionType.PERPHASE); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } long activePowerL1 = ess.setActivePowerL1.peekWrite().get(); long activePowerL2 = ess.setActivePowerL2.peekWrite().get(); @@ -134,8 +131,7 @@ public void test4() { try { power.writePower(ReductionType.PERPHASE); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } long activePowerL1 = ess.setActivePowerL1.peekWrite().get(); long activePowerL2 = ess.setActivePowerL2.peekWrite().get(); @@ -161,8 +157,7 @@ public void test5() { try { power.writePower(ReductionType.PERPHASE); } catch (WriteChannelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.error(e.getMessage()); } long activePowerL1 = ess.setActivePowerL1.peekWrite().get(); long activePowerL2 = ess.setActivePowerL2.peekWrite().get(); diff --git a/edge/src/io/openems/test/utils/channel/UnitTestReadChannel.java b/edge/src/io/openems/test/utils/channel/UnitTestReadChannel.java index 0a5c4572a01..09b17f5cf64 100644 --- a/edge/src/io/openems/test/utils/channel/UnitTestReadChannel.java +++ b/edge/src/io/openems/test/utils/channel/UnitTestReadChannel.java @@ -15,7 +15,6 @@ public String id() { @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } }); diff --git a/edge/src/io/openems/test/utils/channel/UnitTestWriteChannel.java b/edge/src/io/openems/test/utils/channel/UnitTestWriteChannel.java index 9ee51e41a61..27ee9493913 100644 --- a/edge/src/io/openems/test/utils/channel/UnitTestWriteChannel.java +++ b/edge/src/io/openems/test/utils/channel/UnitTestWriteChannel.java @@ -19,7 +19,6 @@ public String id() { @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } }); diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricEssNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricEssNature.java index 8efc1f464fd..8d88aacd878 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricEssNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricEssNature.java @@ -179,31 +179,26 @@ public ReadChannel maxNominalPower() { @Override public Device getParent() { - // TODO Auto-generated method stub return null; } @Override public List getRequiredReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getWriteTasks() { - // TODO Auto-generated method stub return null; } @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricMeterNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricMeterNature.java index ffaa967a7bf..e0abe538d68 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricMeterNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestAsymmetricMeterNature.java @@ -36,7 +36,6 @@ public UnitTestAsymmetricMeterNature(String id) { @Override public ConfigChannel type() { - // TODO Auto-generated method stub return null; } @@ -112,43 +111,36 @@ public ReadChannel voltageL3() { @Override public ConfigChannel maxActivePower() { - // TODO Auto-generated method stub return null; } @Override public ConfigChannel minActivePower() { - // TODO Auto-generated method stub return null; } @Override public Device getParent() { - // TODO Auto-generated method stub return null; } @Override public List getRequiredReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getWriteTasks() { - // TODO Auto-generated method stub return null; } @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java index 20c02d43764..f61f48b374c 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricEssNature.java @@ -136,31 +136,26 @@ public ReadChannel capacity() { @Override public Device getParent() { - // TODO Auto-generated method stub return null; } @Override public List getRequiredReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getWriteTasks() { - // TODO Auto-generated method stub return null; } @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } diff --git a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricMeterNature.java b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricMeterNature.java index da69153a36b..3544418e0e8 100644 --- a/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricMeterNature.java +++ b/edge/src/io/openems/test/utils/devicenatures/UnitTestSymmetricMeterNature.java @@ -27,7 +27,6 @@ public UnitTestSymmetricMeterNature(String id) { @Override public ConfigChannel type() { - // TODO Auto-generated method stub return null; } @@ -48,13 +47,11 @@ public ReadChannel activePower() { @Override public ConfigChannel maxActivePower() { - // TODO Auto-generated method stub return null; } @Override public ConfigChannel minActivePower() { - // TODO Auto-generated method stub return null; } @@ -80,32 +77,26 @@ public ReadChannel voltage() { @Override public Device getParent() { - // TODO Auto-generated method stub return null; } @Override public List getRequiredReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getReadTasks() { - // TODO Auto-generated method stub return null; } @Override public List getWriteTasks() { - // TODO Auto-generated method stub return null; } @Override public ThingStateChannels getStateChannel() { - // TODO Auto-generated method stub return null; } - } diff --git a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java index 33ccdcd2171..c51b329853d 100644 --- a/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java +++ b/io.openems.backend.uiwebsocket.impl.provider/src/io/openems/backend/uiwebsocket/impl/provider/UiWebsocketServer.java @@ -51,7 +51,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { user = this.parent.metadataService.getUserWithSession(sessionIdOpt.get()); } catch (OpenemsException e) { // send connection failed to browser - WebSocketUtils.sendOrLogError(websocket, DefaultMessages.uiConnectionFailedReply()); + WebSocketUtils.sendOrLogError(websocket, DefaultMessages.uiLogoutReply()); log.warn("User connection failed. Session [" + sessionIdOpt.orElse("") + "] Error [" + e.getMessage() + "]."); websocket.closeConnection(CloseFrame.REFUSE, e.getMessage()); @@ -82,7 +82,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { } } log.info("User [" + user.getName() + "] connected with Session [" + sessionIdOpt.orElse("") + "]."); - JsonObject jReply = DefaultMessages.uiConnectionSuccessfulReply("" /* empty token? */, jEdges); + JsonObject jReply = DefaultMessages.uiLoginSuccessfulReply("" /* empty token? */, jEdges); WebSocketUtils.sendOrLogError(websocket, jReply); } diff --git a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java index 93261203d7e..e193ef4468b 100644 --- a/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/CurrentDataWorker.java @@ -37,7 +37,7 @@ public abstract class CurrentDataWorker { */ private Optional> futureOpt = Optional.empty(); - private final WebSocket websocket; + protected final WebSocket websocket; public CurrentDataWorker(WebSocket websocket) { this.websocket = websocket; diff --git a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java index 5871a99e1c1..8161c3fdf4b 100644 --- a/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java +++ b/io.openems.common/src/io/openems/common/websocket/DefaultMessages.java @@ -59,7 +59,7 @@ private static JsonObject newMessage(JsonObject jMessageId) { * @param token * @return */ - public static JsonObject uiConnectionSuccessfulReply(String token, JsonArray jEdges) { + public static JsonObject uiLoginSuccessfulReply(String token, JsonArray jEdges) { JsonObject jAuthenticate = new JsonObject(); jAuthenticate.addProperty("mode", "allow"); jAuthenticate.addProperty("token", token); @@ -83,7 +83,7 @@ public static JsonObject uiConnectionSuccessfulReply(String token, JsonArray jEd * @param token * @return */ - public static JsonObject uiConnectionFailedReply() { + public static JsonObject uiLogoutReply() { JsonObject jAuthenticate = new JsonObject(); jAuthenticate.addProperty("mode", "deny"); JsonObject j = new JsonObject(); diff --git a/io.openems.common/src/io/openems/common/websocket/Notification.java b/io.openems.common/src/io/openems/common/websocket/Notification.java index b09617cd275..6d8c05c4058 100644 --- a/io.openems.common/src/io/openems/common/websocket/Notification.java +++ b/io.openems.common/src/io/openems/common/websocket/Notification.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; public enum Notification { + ERROR(1, NotificationType.ERROR, "Error: %s"), // EDGE_CONNECTION_ClOSED(100, NotificationType.WARNING, "Connection [%s] was interrupted"), // EDGE_CONNECTION_OPENED(101, NotificationType.INFO, "Connection [%s] was established"), // EDGE_UNABLE_TO_FORWARD(102, NotificationType.ERROR, "Unable to forward command to [%s]: %s"), // @@ -18,7 +19,14 @@ public enum Notification { BACKEND_UNABLE_TO_READ_USER_DETAILS(111, NotificationType.ERROR, "Unable to read details for User [ID:%s]"), // METADATA_ERROR(112, NotificationType.ERROR, "Metadata operation failed: %s"), // UNKNOWN_MESSAGE(113, NotificationType.WARNING, "Unknown message. Source [%s]. Message: %s"), // - SUBSCRIBE_CURRENT_DATA_FAILED(114, NotificationType.ERROR, "Subscription to current data failed: %s"); + SUBSCRIBE_CURRENT_DATA_FAILED(114, NotificationType.ERROR, "Subscription to current data failed: %s"), // + CHANNEL_ACCESS_DENIED(115, NotificationType.WARNING, "Access to channel [%s] was denied for User [%s]"), // + VALUE_CONVERSION_FAILED(116, NotificationType.ERROR, "Channel [%s] conversion failed: %s"), // + CHANNEL_NOT_FOUND(117, NotificationType.ERROR, "Channel [%s] not found."), // + UNABLE_TO_SUBSCRIBE_TO_LOG(118, NotificationType.ERROR, "Unable to subscribe to log: %s"), // + UNABLE_TO_EXECUTE_SYSTEM_COMMAND(119, NotificationType.ERROR, "Unable to execute system command: %s"), // + NO_TIMEDATA_SOURCE_AVAILABLE(120, NotificationType.ERROR, "No timedata source available"), + UNABLE_TO_READ_CURRENT_CONFIG(121, NotificationType.ERROR, "Unable to read current config: %s"); private final int value; private final NotificationType status; From 36756ead645eb1494b341a43bb83fb4e4a3b7dd6 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 4 Mar 2018 20:57:30 +0100 Subject: [PATCH 118/156] Repair DebugLogController --- .../openems/impl/controller/debuglog/DebugLogController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java b/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java index d1e8fb3389b..32aec99d5ec 100644 --- a/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java +++ b/edge/src/io/openems/impl/controller/debuglog/DebugLogController.java @@ -26,11 +26,14 @@ import io.openems.api.channel.thingstate.ThingStateChannels; import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; +import io.openems.api.doc.ThingInfo; import io.openems.api.exception.InvalidValueException; +@ThingInfo(title = "Output debugging information on systemlog") public class DebugLogController extends Controller { private ThingStateChannels thingState = new ThingStateChannels(this); + /* * Constructors */ From 3e0ff83d361309e0f1ff9b9b4331450cb82c1a67 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 4 Mar 2018 20:57:45 +0100 Subject: [PATCH 119/156] Implement Metadata Dummy provider --- .../.classpath | 8 ++ .../.gitignore | 3 + .../.project | 23 +++++ .../org.eclipse.core.resources.prefs | 7 ++ .../.settings/org.eclipse.jdt.core.prefs | 11 +++ .../bnd.bnd | 24 +++++ .../debug.bndrun | 13 +++ ...ems.backend.metadata.dummy.provider.bndrun | 14 +++ .../readme.md | 8 ++ .../openems/backend/metadata/dummy/Dummy.java | 97 +++++++++++++++++++ .../backend/metadata/dummy/package-info.java | 2 + .../metadata/dummy/provider/DummyTest.java | 5 + 12 files changed, 215 insertions(+) create mode 100644 io.openems.backend.metadata.dummy.provider/.classpath create mode 100644 io.openems.backend.metadata.dummy.provider/.gitignore create mode 100644 io.openems.backend.metadata.dummy.provider/.project create mode 100644 io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.metadata.dummy.provider/bnd.bnd create mode 100644 io.openems.backend.metadata.dummy.provider/debug.bndrun create mode 100644 io.openems.backend.metadata.dummy.provider/io.openems.backend.metadata.dummy.provider.bndrun create mode 100644 io.openems.backend.metadata.dummy.provider/readme.md create mode 100644 io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/Dummy.java create mode 100644 io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/package-info.java create mode 100644 io.openems.backend.metadata.dummy.provider/test/io/openems/backend/metadata/dummy/provider/DummyTest.java diff --git a/io.openems.backend.metadata.dummy.provider/.classpath b/io.openems.backend.metadata.dummy.provider/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.metadata.dummy.provider/.gitignore b/io.openems.backend.metadata.dummy.provider/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.metadata.dummy.provider/.project b/io.openems.backend.metadata.dummy.provider/.project new file mode 100644 index 00000000000..9ffc26e95de --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.metadata.dummy.provider + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..312d5a53c74 --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/metadata/dummy/Dummy.java=UTF-8 +encoding//test/io/openems/backend/metadata/dummy/provider/DummyTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.metadata.dummy.provider.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.metadata.dummy.provider/bnd.bnd b/io.openems.backend.metadata.dummy.provider/bnd.bnd new file mode 100644 index 00000000000..610d6ea3f7f --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/bnd.bnd @@ -0,0 +1,24 @@ +# +# io.openems.backend.metadata.dummy.provider PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + +Export-Package: io.openems.backend.metadata.api + + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.common;version=latest,\ + com.google.gson;version=2.8,\ + io.openems.backend.edgewebsocket.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +-runbundles: org.apache.felix.log;version='[1.0.1,1.0.2)' +Private-Package: io.openems.backend.metadata.dummy \ No newline at end of file diff --git a/io.openems.backend.metadata.dummy.provider/debug.bndrun b/io.openems.backend.metadata.dummy.provider/debug.bndrun new file mode 100644 index 00000000000..ef3f352218a --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.metadata.dummy.provider DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.metadata.dummy.provider.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.metadata.dummy.provider/io.openems.backend.metadata.dummy.provider.bndrun b/io.openems.backend.metadata.dummy.provider/io.openems.backend.metadata.dummy.provider.bndrun new file mode 100644 index 00000000000..7d62a3b6116 --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/io.openems.backend.metadata.dummy.provider.bndrun @@ -0,0 +1,14 @@ +# +# io.openems.backend.metadata.dummy.provider LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.metadata.dummy.provider.launch +JPM-Command: provider + + +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.dummy.provider.provider)' + +-runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.metadata.dummy.provider/readme.md b/io.openems.backend.metadata.dummy.provider/readme.md new file mode 100644 index 00000000000..5ab37cc7012 --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.metadata.dummy.provider Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/Dummy.java b/io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/Dummy.java new file mode 100644 index 00000000000..6eaa13c83bd --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/Dummy.java @@ -0,0 +1,97 @@ +package io.openems.backend.metadata.dummy; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; +import io.openems.backend.metadata.api.Edge; +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.metadata.api.User; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; +import io.openems.common.utils.StringUtils; + +@Component(name = "io.openems.backend.metadata.dummy.provider") +public class Dummy implements MetadataService { + + private final Logger log = LoggerFactory.getLogger(Dummy.class); + + private int nextUserId = 0; + private int nextEdgeId = 0; + + private Map users = new HashMap<>(); + private Map edges = new HashMap<>(); + + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + private volatile EdgeWebsocketService edgeWebsocketService; + + @Activate + void activate() { + log.debug("Activate MetadataDummy"); + this.nextUserId = 0; + this.nextEdgeId = 0; + this.users.clear(); + this.edges.clear(); + } + + @Deactivate + void deactivate() { + log.debug("Deactivate MetadataDummy"); + } + + @Override + public User getUserWithSession(String sessionId) throws OpenemsException { + int id = this.nextUserId++; + User user = new User(id, "USER:" + sessionId); + for (int edgeId : this.edges.keySet()) { + user.addEdgeRole(edgeId, Role.ADMIN); + } + this.users.put(id, user); + return user; + } + + @Override + public int[] getEdgeIdsForApikey(String apikey) { + Optional edgeOpt = this.getEdgeOpt(this.nextEdgeId); + return new int[] { edgeOpt.get().getId() }; + } + + @Override + public Optional getEdgeOpt(int edgeId) { + Edge edge = this.edges.get(edgeId); + if (edge == null) { + int id = this.nextEdgeId++; + edge = new Edge(id, "EDGE:" + id, "comment [" + id + "]", "producttype [" + id + "]", new JsonObject()); + edge.onSetConfig(jConfig -> { + log.debug("Edge [" + edgeId + "]. Update config: " + StringUtils.toShortString(jConfig, 100)); + }); + edge.onSetSoc(soc -> { + log.debug("Edge [" + edgeId + "]. Set SoC: " + soc); + }); + edge.onSetIpv4(ipv4 -> { + log.debug("Edge [" + edgeId + "]. Set IPv4: " + ipv4); + }); + edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); + this.edges.put(id, edge); + } + return Optional.of(edge); + } + + @Override + public Optional getUser(int userId) { + return Optional.ofNullable(this.users.get(userId)); + } + +} diff --git a/io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/package-info.java b/io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/package-info.java new file mode 100644 index 00000000000..c6878b5db49 --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/src/io/openems/backend/metadata/dummy/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package io.openems.backend.metadata.dummy; diff --git a/io.openems.backend.metadata.dummy.provider/test/io/openems/backend/metadata/dummy/provider/DummyTest.java b/io.openems.backend.metadata.dummy.provider/test/io/openems/backend/metadata/dummy/provider/DummyTest.java new file mode 100644 index 00000000000..cf0070b1b3e --- /dev/null +++ b/io.openems.backend.metadata.dummy.provider/test/io/openems/backend/metadata/dummy/provider/DummyTest.java @@ -0,0 +1,5 @@ +package io.openems.backend.metadata.dummy.provider; + +public class DummyTest { + +} From 1c3d7faec884ef8e0ed424edc3cfb9b78617a110 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 4 Mar 2018 21:45:59 +0100 Subject: [PATCH 120/156] Implement Metadata File provider --- .../.classpath | 8 + .../.gitignore | 3 + .../.project | 23 +++ .../org.eclipse.core.resources.prefs | 7 + .../.settings/org.eclipse.jdt.core.prefs | 11 ++ .../bnd.bnd | 24 +++ .../debug.bndrun | 13 ++ ...nems.backend.metadata.file.provider.bndrun | 14 ++ .../readme.md | 8 + .../backend/metadata/file/provider/File.java | 161 ++++++++++++++++++ .../metadata/file/provider/MyEdge.java | 27 +++ .../metadata/file/provider/FileTest.java | 5 + 12 files changed, 304 insertions(+) create mode 100644 io.openems.backend.metadata.file.provider/.classpath create mode 100644 io.openems.backend.metadata.file.provider/.gitignore create mode 100644 io.openems.backend.metadata.file.provider/.project create mode 100644 io.openems.backend.metadata.file.provider/.settings/org.eclipse.core.resources.prefs create mode 100644 io.openems.backend.metadata.file.provider/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.openems.backend.metadata.file.provider/bnd.bnd create mode 100644 io.openems.backend.metadata.file.provider/debug.bndrun create mode 100644 io.openems.backend.metadata.file.provider/io.openems.backend.metadata.file.provider.bndrun create mode 100644 io.openems.backend.metadata.file.provider/readme.md create mode 100644 io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/File.java create mode 100644 io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/MyEdge.java create mode 100644 io.openems.backend.metadata.file.provider/test/io/openems/backend/metadata/file/provider/FileTest.java diff --git a/io.openems.backend.metadata.file.provider/.classpath b/io.openems.backend.metadata.file.provider/.classpath new file mode 100644 index 00000000000..26009f42341 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io.openems.backend.metadata.file.provider/.gitignore b/io.openems.backend.metadata.file.provider/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.backend.metadata.file.provider/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.backend.metadata.file.provider/.project b/io.openems.backend.metadata.file.provider/.project new file mode 100644 index 00000000000..a6bf4271e34 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/.project @@ -0,0 +1,23 @@ + + + io.openems.backend.metadata.file.provider + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.backend.metadata.file.provider/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.file.provider/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..6b91afef542 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/io/openems/backend/metadata/file/provider/File.java=UTF-8 +encoding//test/io/openems/backend/metadata/file/provider/FileTest.java=UTF-8 +encoding/bnd.bnd=UTF-8 +encoding/debug.bndrun=UTF-8 +encoding/io.openems.backend.metadata.file.provider.bndrun=UTF-8 +encoding/readme.md=UTF-8 diff --git a/io.openems.backend.metadata.file.provider/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.metadata.file.provider/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a21537071b --- /dev/null +++ b/io.openems.backend.metadata.file.provider/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.metadata.file.provider/bnd.bnd b/io.openems.backend.metadata.file.provider/bnd.bnd new file mode 100644 index 00000000000..6ca38fccc20 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/bnd.bnd @@ -0,0 +1,24 @@ +# +# io.openems.backend.metadata.file.provider PROVIDER BUNDLE +# + +Bundle-Version: 1.0.0.${tstamp} + + +Private-Package: \ + io.openems.backend.metadata.file.provider + +-includeresource: {readme.md} + +-buildpath: \ + osgi.enroute.base.api;version=2.1,\ + io.openems.backend.metadata.api;version=latest,\ + io.openems.common;version=latest,\ + com.google.gson;version=2.8,\ + io.openems.backend.edgewebsocket.api;version=latest + +-testpath: \ + osgi.enroute.junit.wrapper;version=4.12, \ + osgi.enroute.hamcrest.wrapper;version=1.3 + +Export-Package: io.openems.backend.metadata.api \ No newline at end of file diff --git a/io.openems.backend.metadata.file.provider/debug.bndrun b/io.openems.backend.metadata.file.provider/debug.bndrun new file mode 100644 index 00000000000..45eb5354cb3 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/debug.bndrun @@ -0,0 +1,13 @@ +# +# io.openems.backend.metadata.file.provider DEBUG LAUNCH SPECFICATION +# + +-include: ~io.openems.backend.metadata.file.provider.bndrun + +-runrequires.debug: \ + ${debug-bundles} + +-runtrace: true + +-runbundles: \ + ${error;Resolve first} diff --git a/io.openems.backend.metadata.file.provider/io.openems.backend.metadata.file.provider.bndrun b/io.openems.backend.metadata.file.provider/io.openems.backend.metadata.file.provider.bndrun new file mode 100644 index 00000000000..2ce6b6322f4 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/io.openems.backend.metadata.file.provider.bndrun @@ -0,0 +1,14 @@ +# +# io.openems.backend.metadata.file.provider LAUNCH SPECIFICATION +# + + +Bundle-Version: 1.0.0.${tstamp} +Bundle-SymbolicName: io.openems.backend.metadata.file.provider.launch +JPM-Command: provider + + +-runrequires: \ + osgi.identity;filter:='(osgi.identity=io.openems.backend.metadata.file.provider.provider)' + +-runbundles: ${error;You must first resolve this bndrun file before you can run it} diff --git a/io.openems.backend.metadata.file.provider/readme.md b/io.openems.backend.metadata.file.provider/readme.md new file mode 100644 index 00000000000..8c363e06930 --- /dev/null +++ b/io.openems.backend.metadata.file.provider/readme.md @@ -0,0 +1,8 @@ +# io.openems.backend.metadata.file.provider Provider + +${Bundle-Description} + +## Example + +## References + diff --git a/io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/File.java b/io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/File.java new file mode 100644 index 00000000000..cb89b0db03a --- /dev/null +++ b/io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/File.java @@ -0,0 +1,161 @@ +package io.openems.backend.metadata.file.provider; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +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.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +import io.openems.backend.edgewebsocket.api.EdgeWebsocketService; +import io.openems.backend.metadata.api.Edge; +import io.openems.backend.metadata.api.MetadataService; +import io.openems.backend.metadata.api.User; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; +import io.openems.common.utils.StringUtils; + +import org.osgi.service.metatype.annotations.Designate; + +/** + * This implementation of MetadataService reads Edges configuration from a + * csv-file. The layout of the fil is as follows: + * + *
        + * 	name;comment;producttype;role;edgeId;apikey
        + * 
        + * + * This implementation does not require any login. It always serves the same + * user, which is has 'role'-permissions on all given Edges. + */ +@Designate(ocd = File.Config.class, factory = true) +@Component(name = "MetadataFile", configurationPolicy = ConfigurationPolicy.REQUIRE) +public class File implements MetadataService { + + private final Logger log = LoggerFactory.getLogger(File.class); + + @ObjectClassDefinition + @interface Config { + String path(); + } + + private String path = ""; + + private User user = null; + private Map edges = new HashMap<>(); + + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + private volatile EdgeWebsocketService edgeWebsocketService; + + @Activate + void activate(Config config) { + log.debug("Activate MetadataFile [path=" + config.path() + "]"); + this.path = config.path(); + this.edges.clear(); + } + + @Deactivate + void deactivate() { + log.debug("Deactivate MetadataFile"); + } + + private void refreshData() { + if (this.edges.isEmpty()) { + try { + // read file + FileReader fr = new FileReader(this.path); + BufferedReader br = new BufferedReader(fr); + String s; + while ((s = br.readLine()) != null) { + try { + String[] parameters = s.split(";"); + String name = parameters[0]; + String comment = parameters[1]; + String producttype = parameters[2]; + Role role = Role.getRole(parameters[3]); + int edgeId = Integer.parseInt(parameters[4]); + String apikey = parameters[5]; + MyEdge edge = new MyEdge(edgeId, name, comment, producttype, role, apikey, new JsonObject()); + edge.onSetConfig(jConfig -> { + log.debug( + "Edge [" + edgeId + "]. Update config: " + StringUtils.toShortString(jConfig, 100)); + }); + edge.onSetSoc(soc -> { + log.debug("Edge [" + edgeId + "]. Set SoC: " + soc); + }); + edge.onSetIpv4(ipv4 -> { + log.debug("Edge [" + edgeId + "]. Set IPv4: " + ipv4); + }); + edge.setOnline(this.edgeWebsocketService.isOnline(edge.getId())); + this.edges.put(edgeId, edge); + } catch (Throwable e) { + log.error("Unable to parse line [" + s + "]. " + e.getClass().getSimpleName() + ": " + + e.getMessage()); + } + } + fr.close(); + } catch (IOException e) { + log.error("Unable to read file [" + this.path + "]: " + e.getMessage()); + } + // refresh user + this.user = new User(0, "admin"); + for (int edgeId : this.edges.keySet()) { + this.user.addEdgeRole(edgeId, Role.ADMIN); + } + } + } + + @Override + public User getUserWithSession(String sessionId) throws OpenemsException { + this.refreshData(); + return this.user; + } + + @Override + public int[] getEdgeIdsForApikey(String apikey) { + this.refreshData(); + List ids = new ArrayList<>(); + for (MyEdge edge : this.edges.values()) { + if (edge.getApikey().equals(apikey)) { + ids.add(edge.getId()); + } + } + int[] result = new int[ids.size()]; + for (int i = 0; i < ids.size(); i++) { + result[i] = ids.get(i); + } + return result; + } + + @Override + public Optional getEdgeOpt(int edgeId) { + this.refreshData(); + return Optional.ofNullable(this.edges.get(edgeId)); + } + + @Override + public Optional getUser(int userId) { + this.refreshData(); + if (this.user != null && userId == this.user.getId()) { + return Optional.of(this.user); + } else { + return Optional.empty(); + } + } + +} diff --git a/io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/MyEdge.java b/io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/MyEdge.java new file mode 100644 index 00000000000..0f31af394bc --- /dev/null +++ b/io.openems.backend.metadata.file.provider/src/io/openems/backend/metadata/file/provider/MyEdge.java @@ -0,0 +1,27 @@ +package io.openems.backend.metadata.file.provider; + +import com.google.gson.JsonObject; + +import io.openems.backend.metadata.api.Edge; +import io.openems.common.session.Role; + +public class MyEdge extends Edge { + + private final String apikey; + private final Role role; + + public MyEdge(int id, String name, String comment, String producttype, Role role, String apikey, + JsonObject jConfig) { + super(id, name, comment, producttype, jConfig); + this.role = role; + this.apikey = apikey; + } + + public String getApikey() { + return apikey; + } + + public Role getRole() { + return role; + } +} diff --git a/io.openems.backend.metadata.file.provider/test/io/openems/backend/metadata/file/provider/FileTest.java b/io.openems.backend.metadata.file.provider/test/io/openems/backend/metadata/file/provider/FileTest.java new file mode 100644 index 00000000000..a3167a277cf --- /dev/null +++ b/io.openems.backend.metadata.file.provider/test/io/openems/backend/metadata/file/provider/FileTest.java @@ -0,0 +1,5 @@ +package io.openems.backend.metadata.file.provider; + +public class FileTest { + +} From e95b1b341ac3e7afcca8eab6401d552667a69a13 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Sun, 4 Mar 2018 21:46:54 +0100 Subject: [PATCH 121/156] Remove obsolete pre-OSGi 'backend' project --- backend/.classpath | 27 -- backend/.gitignore | 13 - backend/.project | 23 - .../org.eclipse.core.resources.prefs | 5 - backend/.settings/org.eclipse.jdt.core.prefs | 409 ------------------ backend/.settings/org.eclipse.jdt.ui.prefs | 119 ----- backend/.settings/org.eclipse.m2e.core.prefs | 4 - backend/pom.xml | 114 ----- backend/resources/logback.xml | 36 -- .../dummy/device/MetadataDummyDevice.java | 63 --- .../device/MetadataDummyDeviceModel.java | 43 -- .../file/device/MetadataFileDevice.java | 60 --- .../file/device/MetadataFileDeviceModel.java | 42 -- 13 files changed, 958 deletions(-) delete mode 100644 backend/.classpath delete mode 100644 backend/.gitignore delete mode 100644 backend/.project delete mode 100644 backend/.settings/org.eclipse.core.resources.prefs delete mode 100644 backend/.settings/org.eclipse.jdt.core.prefs delete mode 100644 backend/.settings/org.eclipse.jdt.ui.prefs delete mode 100644 backend/.settings/org.eclipse.m2e.core.prefs delete mode 100644 backend/pom.xml delete mode 100644 backend/resources/logback.xml delete mode 100644 backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java delete mode 100644 backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java delete mode 100644 backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java delete mode 100644 backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java diff --git a/backend/.classpath b/backend/.classpath deleted file mode 100644 index 393464cc051..00000000000 --- a/backend/.classpath +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index f01a61cf144..00000000000 --- a/backend/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -/target/ -config.properties \ No newline at end of file diff --git a/backend/.project b/backend/.project deleted file mode 100644 index dc292d6fae8..00000000000 --- a/backend/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - backend - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/backend/.settings/org.eclipse.core.resources.prefs b/backend/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 03d9bd7c36d..00000000000 --- a/backend/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/impl/scheduler/SimpleScheduler.java=UTF-8 -encoding//src/main/java=UTF-8 -encoding/=UTF-8 -encoding/src=UTF-8 diff --git a/backend/.settings/org.eclipse.jdt.core.prefs b/backend/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 75e64cd979e..00000000000 --- a/backend/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,409 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullable.secondary= -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=error -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore -org.eclipse.jdt.core.compiler.problem.potentialNullReference=error -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=info -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.8 -org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 -org.eclipse.jdt.core.formatter.align_type_members_on_columns=false -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_assignment=0 -org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 -org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 -org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 -org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 -org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 -org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 -org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 -org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 -org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 -org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_after_package=1 -org.eclipse.jdt.core.formatter.blank_lines_before_field=0 -org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 -org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 -org.eclipse.jdt.core.formatter.blank_lines_before_method=1 -org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 -org.eclipse.jdt.core.formatter.blank_lines_before_package=0 -org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 -org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 -org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false -org.eclipse.jdt.core.formatter.comment.format_block_comments=true -org.eclipse.jdt.core.formatter.comment.format_header=false -org.eclipse.jdt.core.formatter.comment.format_html=true -org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true -org.eclipse.jdt.core.formatter.comment.format_line_comments=true -org.eclipse.jdt.core.formatter.comment.format_source_code=true -org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true -org.eclipse.jdt.core.formatter.comment.indent_root_tags=true -org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert -org.eclipse.jdt.core.formatter.comment.line_length=120 -org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true -org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true -org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false -org.eclipse.jdt.core.formatter.compact_else_if=true -org.eclipse.jdt.core.formatter.continuation_indentation=2 -org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 -org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off -org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on -org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false -org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true -org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_empty_lines=false -org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true -org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false -org.eclipse.jdt.core.formatter.indentation.size=4 -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert -org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert -org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert -org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.join_lines_in_comments=false -org.eclipse.jdt.core.formatter.join_wrapped_lines=true -org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false -org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false -org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false -org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false -org.eclipse.jdt.core.formatter.lineSplit=120 -org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false -org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 -org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 -org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines -org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true -org.eclipse.jdt.core.formatter.tabulation.char=tab -org.eclipse.jdt.core.formatter.tabulation.size=4 -org.eclipse.jdt.core.formatter.use_on_off_tags=false -org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false -org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false -org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true -org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true -org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true -org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true -org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/backend/.settings/org.eclipse.jdt.ui.prefs b/backend/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index bc6b39f5579..00000000000 --- a/backend/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,119 +0,0 @@ -cleanup.add_default_serial_version_id=true -cleanup.add_generated_serial_version_id=false -cleanup.add_missing_annotations=true -cleanup.add_missing_deprecated_annotations=true -cleanup.add_missing_methods=false -cleanup.add_missing_nls_tags=false -cleanup.add_missing_override_annotations=true -cleanup.add_missing_override_annotations_interface_methods=true -cleanup.add_serial_version_id=false -cleanup.always_use_blocks=true -cleanup.always_use_parentheses_in_expressions=false -cleanup.always_use_this_for_non_static_field_access=false -cleanup.always_use_this_for_non_static_method_access=true -cleanup.convert_functional_interfaces=false -cleanup.convert_to_enhanced_for_loop=false -cleanup.correct_indentation=false -cleanup.format_source_code=false -cleanup.format_source_code_changes_only=false -cleanup.insert_inferred_type_arguments=false -cleanup.make_local_variable_final=true -cleanup.make_parameters_final=false -cleanup.make_private_fields_final=true -cleanup.make_type_abstract_if_missing_method=false -cleanup.make_variable_declarations_final=false -cleanup.never_use_blocks=false -cleanup.never_use_parentheses_in_expressions=true -cleanup.organize_imports=false -cleanup.qualify_static_field_accesses_with_declaring_class=false -cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -cleanup.qualify_static_member_accesses_with_declaring_class=true -cleanup.qualify_static_method_accesses_with_declaring_class=false -cleanup.remove_private_constructors=true -cleanup.remove_redundant_type_arguments=false -cleanup.remove_trailing_whitespaces=false -cleanup.remove_trailing_whitespaces_all=true -cleanup.remove_trailing_whitespaces_ignore_empty=false -cleanup.remove_unnecessary_casts=true -cleanup.remove_unnecessary_nls_tags=true -cleanup.remove_unused_imports=true -cleanup.remove_unused_local_variables=false -cleanup.remove_unused_private_fields=true -cleanup.remove_unused_private_members=false -cleanup.remove_unused_private_methods=true -cleanup.remove_unused_private_types=true -cleanup.sort_members=false -cleanup.sort_members_all=false -cleanup.use_anonymous_class_creation=false -cleanup.use_blocks=false -cleanup.use_blocks_only_for_return_and_throw=false -cleanup.use_lambda=true -cleanup.use_parentheses_in_expressions=false -cleanup.use_this_for_non_static_field_access=false -cleanup.use_this_for_non_static_field_access_only_if_necessary=true -cleanup.use_this_for_non_static_method_access=true -cleanup.use_this_for_non_static_method_access_only_if_necessary=false -cleanup_profile=_OpenEMS -cleanup_settings_version=2 -eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true -formatter_profile=_OpenEMS -formatter_settings_version=12 -sp_cleanup.add_default_serial_version_id=true -sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=true -sp_cleanup.add_missing_deprecated_annotations=true -sp_cleanup.add_missing_methods=false -sp_cleanup.add_missing_nls_tags=false -sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=true -sp_cleanup.add_serial_version_id=false -sp_cleanup.always_use_blocks=true -sp_cleanup.always_use_parentheses_in_expressions=false -sp_cleanup.always_use_this_for_non_static_field_access=false -sp_cleanup.always_use_this_for_non_static_method_access=false -sp_cleanup.convert_functional_interfaces=false -sp_cleanup.convert_to_enhanced_for_loop=false -sp_cleanup.correct_indentation=true -sp_cleanup.format_source_code=true -sp_cleanup.format_source_code_changes_only=false -sp_cleanup.insert_inferred_type_arguments=false -sp_cleanup.make_local_variable_final=true -sp_cleanup.make_parameters_final=false -sp_cleanup.make_private_fields_final=true -sp_cleanup.make_type_abstract_if_missing_method=false -sp_cleanup.make_variable_declarations_final=false -sp_cleanup.never_use_blocks=false -sp_cleanup.never_use_parentheses_in_expressions=true -sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=true -sp_cleanup.qualify_static_field_accesses_with_declaring_class=false -sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_with_declaring_class=false -sp_cleanup.qualify_static_method_accesses_with_declaring_class=false -sp_cleanup.remove_private_constructors=true -sp_cleanup.remove_redundant_type_arguments=false -sp_cleanup.remove_trailing_whitespaces=true -sp_cleanup.remove_trailing_whitespaces_all=true -sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=true -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false -sp_cleanup.remove_unused_local_variables=false -sp_cleanup.remove_unused_private_fields=true -sp_cleanup.remove_unused_private_members=false -sp_cleanup.remove_unused_private_methods=true -sp_cleanup.remove_unused_private_types=true -sp_cleanup.sort_members=false -sp_cleanup.sort_members_all=true -sp_cleanup.use_anonymous_class_creation=false -sp_cleanup.use_blocks=true -sp_cleanup.use_blocks_only_for_return_and_throw=false -sp_cleanup.use_lambda=true -sp_cleanup.use_parentheses_in_expressions=false -sp_cleanup.use_this_for_non_static_field_access=false -sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true -sp_cleanup.use_this_for_non_static_method_access=false -sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/backend/.settings/org.eclipse.m2e.core.prefs b/backend/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1cb2..00000000000 --- a/backend/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/backend/pom.xml b/backend/pom.xml deleted file mode 100644 index 5fe4decb9de..00000000000 --- a/backend/pom.xml +++ /dev/null @@ -1,114 +0,0 @@ - - OpenEMS Backend - Open Source Energy Management System - http://openems.io - io.openems - edge - 2018.2.0-SNAPSHOT - jar - - https://github.com/OpenEMS/openems - scm:git:git://github.com/OpenEMS/openems.git - - - UTF-8 - 2.8.2 - 23.1-jre - 2.7 - 4.8.1 - 1.2.3 - 1.8 - 1.8 - 3.1.0 - 3.0.2 - 2.3.10 - 3.0.1 - 1.7.25 - 1.3.6 - - - - maven-restlet - Public online Restlet repository - http://maven.restlet.com - - - - - com.google.code.gson - gson - ${gson.version} - - - com.google.guava - guava - ${guava.version} - - - org.influxdb - influxdb-java - ${influxdb.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - org.restlet.jse - org.restlet - ${restlet.version} - - - org.restlet.jse - org.restlet.ext.slf4j - ${restlet.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.java-websocket - Java-WebSocket - ${websocket.version} - - - - - - maven-jar-plugin - ${maven-jar-plugin.version} - - - default-jar - none - - - - - maven-assembly-plugin - ${maven-assembly-plugin.version} - - openems-backend - false - - jar-with-dependencies - - - - - make-assembly - package - - single - - - - - - - 4.0.0 - diff --git a/backend/resources/logback.xml b/backend/resources/logback.xml deleted file mode 100644 index 482ab17a2bc..00000000000 --- a/backend/resources/logback.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - [%-8.8thread] [%-5level] [%-20.20logger{36}:%-3line] %msg%ex{10}%n - - - - - - - - - diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java deleted file mode 100644 index ce2d8008f36..00000000000 --- a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDevice.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.openems.backend.metadata.dummy.device; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -// TODO migrate to OSGi -public class MetadataDummyDevice { - - @SuppressWarnings("unused") - private final Logger log = LoggerFactory.getLogger(MetadataDummyDevice.class); - - @SuppressWarnings("unused") - private final int id; - private final String apikey; - - public MetadataDummyDevice(String name, String comment, String producttype, String role, int id, String apikey) { - this.id = id; - this.apikey = apikey; - } - - public String getApikey() { - return apikey; - } - - public String getState() { - return "active"; - } - - // @Override - // public void setOpenemsConfig(JsonObject j) { - // log.info("Metadata Dummy. Would set OpenEMS config: " + j.toString()); - // } - // - // @Override - // public void setState(String state) { - // log.info("Metadata Dummy. Would set state: " + state); - // } - // - // @Override - // public void setSoc(int value) { - // log.info("Metadata Dummy. Would set SOC: " + value); - // } - // - // @Override - // public void setLastMessage() { - // log.info("Metadata Dummy. Would set LastMessage"); - // } - // - // @Override - // public void setLastUpdate() { - // log.debug("Metadata Dummy. Would set LastUpdate"); - // } - // - // @Override - // public void setIpV4(String value) { - // log.info("Metadata Dummy. Would set IPv4: " + value); - // } - // - // @Override - // public void writeObject() throws OpenemsException { - // log.debug("Metadata Dummy. Would write object"); - // } -} diff --git a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java b/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java deleted file mode 100644 index 34506417287..00000000000 --- a/backend/src/main/java/io/openems/backend/metadata/dummy/device/MetadataDummyDeviceModel.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.openems.backend.metadata.dummy.device; - -import java.util.ArrayList; -import java.util.List; - -//TODO migrate to OSGi -public class MetadataDummyDeviceModel { - - private static int lastId = 0; - - private final List devices = new ArrayList<>(); - - public MetadataDummyDeviceModel() {} - - // public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException { - // // filter and convert to new list - // MetadataDevices result = new MetadataDevices(); - // for (MetadataDummyDevice device : this.devices) { - // if (device.getApikey().equals(apikey)) { - // result.add(device); - // } - // } - // // add device if it was not there yet - // if (result.isEmpty()) { - // MetadataDummyDevice device = addNewDevice(apikey); - // result.add(device); - // } - // return result; - // } - - @SuppressWarnings("unused") - private MetadataDummyDevice addNewDevice(String apikey) { - int id = MetadataDummyDeviceModel.lastId++; - MetadataDummyDevice device = new MetadataDummyDevice("openems" + id, "OpenEMS " + id, "Dummy Product", "admin", - id, apikey); - this.devices.add(device); - return device; - } - - public List getAllDevices() { - return this.devices; - } -} diff --git a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java deleted file mode 100644 index ae02680330a..00000000000 --- a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDevice.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.openems.backend.metadata.file.device; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -//TODO migrate to OSGi -public class MetadataFileDevice { - - @SuppressWarnings("unused") - private final Logger log = LoggerFactory.getLogger(MetadataFileDevice.class); - - @SuppressWarnings("unused") - private final int id; - @SuppressWarnings("unused") - private final String apikey; - - public MetadataFileDevice(String name, String comment, String producttype, String role, int id, String apikey) { - this.id = id; - this.apikey = apikey; - } - - public String getState() { - return "active"; - } - - // @Override - // public void setOpenemsConfig(JsonObject j) { - // log.info("Metadata Dummy. Would set OpenEMS config: " + j.toString()); - // } - // - // @Override - // public void setState(String state) { - // log.info("Metadata Dummy. Would set state: " + state); - // } - // - // @Override - // public void setSoc(int value) { - // log.info("Metadata Dummy. Would set SOC: " + value); - // } - // - // @Override - // public void setLastMessage() { - // log.info("Metadata Dummy. Would set LastMessage"); - // } - // - // @Override - // public void setLastUpdate() { - // log.debug("Metadata Dummy. Would set LastUpdate"); - // } - // - // @Override - // public void setIpV4(String value) { - // log.info("Metadata Dummy. Would set IPv4: " + value); - // } - // - // @Override - // public void writeObject() throws OpenemsException { - // log.debug("Metadata Dummy. Would write object"); - // } -} diff --git a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java b/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java deleted file mode 100644 index 187b6beddf7..00000000000 --- a/backend/src/main/java/io/openems/backend/metadata/file/device/MetadataFileDeviceModel.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.openems.backend.metadata.file.device; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class MetadataFileDeviceModel { - - private final List devices = new ArrayList<>(); - - public MetadataFileDeviceModel(File file) throws IOException { - FileReader fr = new FileReader(file); - BufferedReader br = new BufferedReader(fr); - String s; - while ((s = br.readLine()) != null) { - String[] parameters = s.split(";"); - MetadataFileDevice device = new MetadataFileDevice(parameters[0], parameters[1], parameters[2], - parameters[3], Integer.parseInt(parameters[4]), parameters[5]); - this.devices.add(device); - } - fr.close(); - } - - // @Override - // public MetadataDevices getDevicesForApikey(String apikey) throws OpenemsException { - // // filter and convert to new list - // MetadataDevices result = new MetadataDevices(); - // for (MetadataFileDevice device : this.devices) { - // if (device.getApikey().equals(apikey)) { - // result.add(device); - // } - // } - // return result; - // } - - public List getAllDevices() { - return this.devices; - } -} From e10b7d9dc63464a30bca258620dceb7e1aaa37b5 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Mar 2018 10:08:18 +0100 Subject: [PATCH 122/156] Move loggingConfiguration to common bundle --- .../backend/application/BackendApp.java | 28 ++------------- .../io/openems/common/config/ConfigUtils.java | 35 ++++++++++++++++--- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index bf5bc7d4f1c..cece9d1e6cb 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -1,9 +1,5 @@ package io.openems.backend.application; -import java.io.IOException; -import java.util.Hashtable; - -import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -12,6 +8,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openems.common.config.ConfigUtils; + @Component() public class BackendApp { @@ -23,27 +21,7 @@ public class BackendApp { @Activate void activate() { log.debug("Activate BackendApp"); - configureLogging(); - } - - private void configureLogging() { - Configuration configuration; - try { - configuration = configAdmin.getConfiguration("org.ops4j.pax.logging", null); - final Hashtable log4jProps = new Hashtable(); - log4jProps.put("log4j.rootLogger", "DEBUG, CONSOLE"); - log4jProps.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); - log4jProps.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); - log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", - "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); - // set minimum log levels for some verbose packages - log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); - log4jProps.put("log4j.logger.org.apache.felix.configadmin", "INFO"); - log4jProps.put("log4j.logger.sun.net.www.protocol.http.HttpURLConnection", "INFO"); - configuration.update(log4jProps); - } catch (IOException e1) { - e1.printStackTrace(); - } + ConfigUtils.configureLogging(configAdmin); } @Deactivate diff --git a/io.openems.common/src/io/openems/common/config/ConfigUtils.java b/io.openems.common/src/io/openems/common/config/ConfigUtils.java index 4d9a2dd8f23..954a3d8f690 100644 --- a/io.openems.common/src/io/openems/common/config/ConfigUtils.java +++ b/io.openems.common/src/io/openems/common/config/ConfigUtils.java @@ -5,11 +5,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Map.Entry; + +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +import java.util.Hashtable; import java.util.TreeMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.common.exceptions.NotImplementedException; @@ -20,10 +24,33 @@ public class ConfigUtils { private final static Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + public static synchronized void configureLogging(ConfigurationAdmin configAdmin) { + Configuration configuration; + try { + configuration = configAdmin.getConfiguration("org.ops4j.pax.logging", null); + final Hashtable log4jProps = new Hashtable(); + log4jProps.put("log4j.rootLogger", "DEBUG, CONSOLE"); + log4jProps.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); + log4jProps.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); + log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", + "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); + // set minimum log levels for some verbose packages + log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); + log4jProps.put("log4j.logger.org.apache.felix.configadmin", "INFO"); + log4jProps.put("log4j.logger.sun.net.www.protocol.http.HttpURLConnection", "INFO"); + configuration.update(log4jProps); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + protected static synchronized JsonObject readConfigFromFile(Path path) throws Exception { - String config = new String(Files.readAllBytes(path), DEFAULT_CHARSET); - JsonElement jsonElement = JsonUtils.parse(config); - return jsonElement.getAsJsonObject(); + try { + String config = new String(Files.readAllBytes(path), DEFAULT_CHARSET); + return JsonUtils.parse(config).getAsJsonObject(); + } catch (Exception e) { + return new JsonObject(); + } } protected static synchronized void writeConfigToFile(Path path, TreeMap configs) From d58b49518dbe0d5f0f843ec25df8b8ff90f071b5 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Mar 2018 11:56:30 +0100 Subject: [PATCH 123/156] Produce error in SimulatorEss --- .../impl/device/simulator/FaultEss.java | 18 ++++++++++++++++++ .../simulator/SimulatorAsymmetricEss.java | 6 ++++++ .../simulator/SimulatorSymmetricEss.java | 3 +-- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 edge/src/io/openems/impl/device/simulator/FaultEss.java diff --git a/edge/src/io/openems/impl/device/simulator/FaultEss.java b/edge/src/io/openems/impl/device/simulator/FaultEss.java new file mode 100644 index 00000000000..7937bfa441d --- /dev/null +++ b/edge/src/io/openems/impl/device/simulator/FaultEss.java @@ -0,0 +1,18 @@ +package io.openems.impl.device.simulator; + +import io.openems.api.channel.thingstate.FaultEnum; + +public enum FaultEss implements FaultEnum { + SimulatedError(0); + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java index ce4f1c34a49..81298179375 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java @@ -32,6 +32,7 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.FunctionalReadChannel; import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.StaticThingStateChannel; import io.openems.api.channel.StaticValueChannel; import io.openems.api.channel.WriteChannel; import io.openems.api.channel.thingstate.ThingStateChannels; @@ -73,6 +74,11 @@ public SimulatorAsymmetricEss(String thingId, Device parent) throws ConfigExcept } }); this.thingState = new ThingStateChannels(this); + + StaticThingStateChannel tmp = new StaticThingStateChannel(FaultEss.SimulatedError, this, false); + tmp.setValue(true); + thingState.addFaultChannel(tmp); + long initialSoc = SimulatorTools.addRandomLong(90, 90, 100, 5); this.energy = capacity.valueOptional().get() / 100 * initialSoc; this.soc = new FunctionalReadChannel("Soc", this, (channels) -> { diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index 076a5c23a14..6cff2e88e82 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -51,7 +51,6 @@ import io.openems.core.ThingRepository; import io.openems.core.utilities.AvgFiFoQueue; import io.openems.core.utilities.ControllerUtils; -import io.openems.impl.protocol.modbus.FaultModbus; import io.openems.impl.protocol.modbus.ModbusWriteLongChannel; import io.openems.impl.protocol.simulator.SimulatorDeviceNature; import io.openems.impl.protocol.simulator.SimulatorReadChannel; @@ -82,7 +81,7 @@ public SimulatorSymmetricEss(String thingId, Device parent) throws ConfigExcepti super(thingId, parent); this.thingState = new ThingStateChannels(this); - StaticThingStateChannel tmp = new StaticThingStateChannel(FaultModbus.ConfigurationFault, this, false); + StaticThingStateChannel tmp = new StaticThingStateChannel(FaultEss.SimulatedError, this, false); tmp.setValue(true); thingState.addFaultChannel(tmp); From d20934bacc177780c064fffb4263042962e99c92 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Mar 2018 11:56:52 +0100 Subject: [PATCH 124/156] Implement first warnings/errors widget --- .../device/overview/overview.component.html | 1 + ui/src/app/device/overview/overview.module.ts | 4 ++- .../overview/state/state.component.html | 35 +++++++++++++++++++ .../device/overview/state/state.component.ts | 25 +++++++++++++ .../haswarningorfault.pipe.ts | 27 ++++++++++++++ ui/src/app/shared/shared.module.ts | 3 ++ 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 ui/src/app/device/overview/state/state.component.html create mode 100644 ui/src/app/device/overview/state/state.component.ts create mode 100644 ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts diff --git a/ui/src/app/device/overview/overview.component.html b/ui/src/app/device/overview/overview.component.html index cb6e5112829..ae6734654e7 100644 --- a/ui/src/app/device/overview/overview.component.html +++ b/ui/src/app/device/overview/overview.component.html @@ -6,6 +6,7 @@ + diff --git a/ui/src/app/device/overview/overview.module.ts b/ui/src/app/device/overview/overview.module.ts index d2e66e82dc5..691bb8f1df6 100644 --- a/ui/src/app/device/overview/overview.module.ts +++ b/ui/src/app/device/overview/overview.module.ts @@ -7,6 +7,7 @@ import { EvcsComponent } from './evcs/evcs.component'; import { HistoryComponent } from './history/history.component'; import { FieldstatusComponent } from './fieldstatus/fieldstatus.component'; import { OverviewComponent } from './overview.component'; +import { StateComponent } from './state/state.component'; @NgModule({ imports: [ @@ -18,7 +19,8 @@ import { OverviewComponent } from './overview.component'; EvcsComponent, EnergytableComponent, HistoryComponent, - FieldstatusComponent + FieldstatusComponent, + StateComponent ] }) export class OverviewModule { } diff --git a/ui/src/app/device/overview/state/state.component.html b/ui/src/app/device/overview/state/state.component.html new file mode 100644 index 00000000000..1576d519caf --- /dev/null +++ b/ui/src/app/device/overview/state/state.component.html @@ -0,0 +1,35 @@ + + +
        + + + error + Warnungen und Fehler + + + + + + + + + + + + + +
        {{ thingId }} + Warnung + Fehler +
        +
        + + + + +
        +
        +
        +
        \ No newline at end of file diff --git a/ui/src/app/device/overview/state/state.component.ts b/ui/src/app/device/overview/state/state.component.ts new file mode 100644 index 00000000000..dea359774ac --- /dev/null +++ b/ui/src/app/device/overview/state/state.component.ts @@ -0,0 +1,25 @@ +import { Component, Input, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +import { Device } from '../../../shared/device/device'; +import { Utils } from '../../../shared/service/utils'; +import { DefaultTypes } from '../../../shared/service/defaulttypes'; +import { CurrentDataAndSummary } from '../../../shared/device/currentdata'; + +@Component({ + selector: 'state', + templateUrl: './state.component.html' +}) +export class StateComponent { + + @Input() + public device: Device; + + @Input() + public currentData: CurrentDataAndSummary; + + @Input() + public config: DefaultTypes.Config; + + constructor(public utils: Utils) { } +} diff --git a/ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts b/ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts new file mode 100644 index 00000000000..96b0e4e9d65 --- /dev/null +++ b/ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts @@ -0,0 +1,27 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import { DefaultTypes } from '../../service/defaulttypes'; + +/** + * Runs a deep search in the given currentData object for a Channel 'State' which is non-equal to zero. + * Use like: *ngFor="let thingId in currentData | haswarningorfault" + */ +@Pipe({ + name: 'haswarningorfault' +}) +export class HasWarningOrFaultPipe implements PipeTransform { + transform(currentData: DefaultTypes.Data): DefaultTypes.Data { + if (currentData == null) { + return {} + } + let result = {} + for (let thingId of Object.keys(currentData.data)) { + let thing = currentData.data[thingId] + if (thing['State'] != 0) { + result[thingId] = thing + } + } + console.log(result) + return result + } +} \ No newline at end of file diff --git a/ui/src/app/shared/shared.module.ts b/ui/src/app/shared/shared.module.ts index 4f925bed93e..852b3153089 100644 --- a/ui/src/app/shared/shared.module.ts +++ b/ui/src/app/shared/shared.module.ts @@ -40,6 +40,7 @@ import { SocChartComponent } from './../device/history/chart/socchart/socchart.c import { AbstractConfigComponent } from './config/abstractconfig.component'; import { ExistingThingComponent } from './config/existingthing.component'; import { ChannelComponent } from './config/channel.component'; +import { HasWarningOrFaultPipe } from './pipe/haswarningorfault/haswarningorfault.pipe'; @NgModule({ imports: [ @@ -63,6 +64,7 @@ import { ChannelComponent } from './config/channel.component'; SignPipe, IsclassPipe, HasclassPipe, + HasWarningOrFaultPipe, // components SocChartComponent, AbstractConfigComponent, @@ -76,6 +78,7 @@ import { ChannelComponent } from './config/channel.component'; ClassnamePipe, IsclassPipe, HasclassPipe, + HasWarningOrFaultPipe, // modules BrowserAnimationsModule, ChartsModule, From b0add7c545c0c6d8d41de2618e8aaf841733e536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 6 Mar 2018 13:30:33 +0100 Subject: [PATCH 125/156] Fix updateState logic if the first childChannel had a Warning and the second a Fault the the ThingStateChannel had returned warning as thingState --- .../thingstate/ThingStateChannels.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java b/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java index a3c18d003d0..8e3a1ef4087 100644 --- a/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java +++ b/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java @@ -102,15 +102,16 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol } private void updateState() { + ThingState currentState = ThingState.RUN; for(ThingStateChannels child : this.childChannels) { if(child.isValuePresent()) { switch(child.getValue()) { case FAULT: - updateValue(ThingState.FAULT); - return; + currentState = ThingState.FAULT; case WARNING: - updateValue(ThingState.WARNING); - return; + if(currentState != ThingState.FAULT) { + currentState = ThingState.WARNING; + } default: break; } @@ -118,17 +119,15 @@ private void updateState() { } for (ThingStateChannel faultChannel : faultChannels) { if (faultChannel.isValuePresent() && faultChannel.getValue()) { - updateValue(ThingState.FAULT); - return; + currentState = ThingState.FAULT; } } for (ThingStateChannel warningChannel : warningChannels) { - if (warningChannel.isValuePresent() && warningChannel.getValue()) { - updateValue(ThingState.WARNING); - return; + if (warningChannel.isValuePresent() && warningChannel.getValue()&¤tState != ThingState.FAULT) { + currentState = ThingState.WARNING; } } - updateValue(ThingState.RUN); + updateValue(currentState); } } From 951c49e15934b0ccf7f37798036f4acc00d836c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 6 Mar 2018 13:32:32 +0100 Subject: [PATCH 126/156] Remove pom --- common/pom.xml | 78 -------------------------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 common/pom.xml diff --git a/common/pom.xml b/common/pom.xml deleted file mode 100644 index dc11a26fd5f..00000000000 --- a/common/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ - - OpenEMS Common - Open Source Energy Management System - http://openems.io - io.openems - common - 2018.2.0-SNAPSHOT - jar - - https://github.com/OpenEMS/openems - scm:git:git://github.com/OpenEMS/openems.git - - - 1.8 - 1.8 - UTF-8 - 2.8.2 - 23.1-jre - 2.7 - 4.8.1 - 1.2.3 - 2.3.10 - 1.1.0.RELEASE - 1.7.25 - 1.3.6 - - - - maven-restlet - Public online Restlet repository - http://maven.restlet.com - - - - - com.google.code.gson - gson - ${gson.version} - - - com.google.guava - guava - ${guava.version} - - - org.influxdb - influxdb-java - ${influxdb.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - org.restlet.jse - org.restlet - ${restlet.version} - - - org.restlet.jse - org.restlet.ext.slf4j - ${restlet.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.java-websocket - Java-WebSocket - ${websocket.version} - - - 4.0.0 - From 1ca2561dd4a80f8523d1b9f18d359218328e4270 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 6 Mar 2018 14:20:04 +0100 Subject: [PATCH 127/156] Implement warningOrFaultQuery() --- .../device/overview/state/state.component.html | 6 +++--- .../device/overview/state/state.component.ts | 18 +++++++++++++++++- ui/src/app/shared/device/device.ts | 12 ++++++++++++ ui/src/app/shared/service/defaultmessages.ts | 13 +++++++++++++ ui/src/app/shared/service/defaulttypes.ts | 7 +++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/ui/src/app/device/overview/state/state.component.html b/ui/src/app/device/overview/state/state.component.html index 1576d519caf..9a10dd1e714 100644 --- a/ui/src/app/device/overview/state/state.component.html +++ b/ui/src/app/device/overview/state/state.component.html @@ -1,5 +1,5 @@ - - + +
        @@ -24,7 +24,7 @@ - + diff --git a/ui/src/app/device/overview/state/state.component.ts b/ui/src/app/device/overview/state/state.component.ts index dea359774ac..c5eba999468 100644 --- a/ui/src/app/device/overview/state/state.component.ts +++ b/ui/src/app/device/overview/state/state.component.ts @@ -15,8 +15,24 @@ export class StateComponent { @Input() public device: Device; + public _currentData: CurrentDataAndSummary = null; + private lastThingIds: string[] + @Input() - public currentData: CurrentDataAndSummary; + set currentData(currentData: CurrentDataAndSummary) { + this._currentData = currentData; + let thingIds = []; + for (let thingId of Object.keys(currentData.data)) { + let thing = currentData.data[thingId]; + if (thing['State'] != 0) { + thingIds.push(thingId); + } + } + console.log(thingIds); + this.device.warningOrFaultQuery(thingIds).then(warningsOrFaults => { + console.log(warningsOrFaults); + }); + } @Input() public config: DefaultTypes.Config; diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts index eb517919dfc..14eca40f47a 100644 --- a/ui/src/app/shared/device/device.ts +++ b/ui/src/app/shared/device/device.ts @@ -145,6 +145,18 @@ export class Device { }) } + public warningOrFaultQuery(thingIds: string[]): Promise { + let replyStream = this.sendMessageWithReply(DefaultMessages.warningOrFaultQuery(this.edgeId, thingIds)); + // wait for reply + return new Promise((resolve, reject) => { + replyStream.first().subscribe(reply => { + let warningsOrFaults = (reply as DefaultMessages.HistoricDataReply).warningsOrFaults; + this.removeReplyStream(reply); + resolve(warningsOrFaults); + }); + }) + } + /** * Mark this device as online or offline * @param online diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts index 72319b0918d..28cff6dd165 100644 --- a/ui/src/app/shared/service/defaultmessages.ts +++ b/ui/src/app/shared/service/defaultmessages.ts @@ -85,6 +85,19 @@ export class DefaultMessages { } }; + public static warningOrFaultQuery(edgeId: number, thingIds: string[]): DefaultTypes.IdentifiedMessage { + return { + messageId: { + ui: UUID.UUID() + }, + edgeId: edgeId, + warningsOrFaults: { + mode: "query", + thingIds: thingIds + } + } + } + public static logSubscribe(edgeId: number): DefaultTypes.IdentifiedMessage { return { messageId: { diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index 9243928ec15..8432e2188f4 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -52,6 +52,13 @@ export module DefaultTypes { }] } + export interface WarningsOrFaults { + [thingId: string]: [{ + level: 'warning' | 'fault', + name: string + }] + } + export interface Summary { storage: { soc: number, From c4ca173fb99264277f67a9cf6626333b397d8799 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:06:07 +0100 Subject: [PATCH 128/156] Introduce ThingStateInfo annotation + format Enums --- .../openems/impl/device/byd/WarningEss.java | 57 ++- .../impl/device/commercial/FaultCharger.java | 455 ++++++++++++++---- .../impl/device/commercial/FaultEss.java | 129 +++-- .../device/commercial/WarningCharger.java | 249 +++++++--- .../impl/device/commercial/WarningEss.java | 135 +++--- .../io/openems/impl/device/mini/FaultEss.java | 93 ++-- .../openems/impl/device/mini/WarningEss.java | 59 ++- .../impl/device/minireadonly/FaultEss.java | 97 ++-- .../impl/device/minireadonly/WarningEss.java | 151 ++++-- .../io/openems/impl/device/pro/FaultEss.java | 2 + .../openems/impl/device/pro/WarningEss.java | 2 + .../impl/device/pro/WarningPvMeter.java | 2 + .../io/openems/impl/device/refu/FaultEss.java | 116 +++-- .../openems/impl/device/refu/WarningEss.java | 84 ++-- .../impl/device/simulator/FaultEss.java | 4 +- .../simulator/SimulatorAsymmetricEss.java | 12 +- .../simulator/SimulatorSymmetricEss.java | 5 - .../impl/device/simulator/SimulatorTools.java | 77 +-- .../impl/protocol/modbus/FaultModbus.java | 4 +- .../openems/common/types/ThingStateInfo.java | 14 + 20 files changed, 1254 insertions(+), 493 deletions(-) create mode 100644 io.openems.common/src/io/openems/common/types/ThingStateInfo.java diff --git a/edge/src/io/openems/impl/device/byd/WarningEss.java b/edge/src/io/openems/impl/device/byd/WarningEss.java index ec153c243d7..61ff5d9377b 100644 --- a/edge/src/io/openems/impl/device/byd/WarningEss.java +++ b/edge/src/io/openems/impl/device/byd/WarningEss.java @@ -1,22 +1,35 @@ -package io.openems.impl.device.byd; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum { - - WarningState(0), ProtectionState(1), DeratingState(2), ChargeForbidden(3), DischargeForbidden(4), StatusAbnormalOfACSurgeProtector(5), - CloseOfControlSwitch(6), EmergencyStop(7), StatusAbnormalOfFrogDetector(8), SeriousLeakage(9), NormalLeakage(10), - FailureOfTemperatureSensorInControlCabinet(11), FailureOfHumiditySensorInControlCabinet(12), FailureOfStorageDevice(13), - ExceedingOfHumidityInControlCabinet(14); - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.byd; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = Bem125ktla01Ess.class) +public enum WarningEss implements WarningEnum { + + WarningState(0), // + ProtectionState(1), // + DeratingState(2), // + ChargeForbidden(3), // + DischargeForbidden(4), // + StatusAbnormalOfACSurgeProtector(5), // + CloseOfControlSwitch(6), // + EmergencyStop(7), // + StatusAbnormalOfFrogDetector(8), // + SeriousLeakage(9), // + NormalLeakage(10), // + FailureOfTemperatureSensorInControlCabinet(11), // + FailureOfHumiditySensorInControlCabinet(12), // + FailureOfStorageDevice(13), // + ExceedingOfHumidityInControlCabinet(14); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/FaultCharger.java b/edge/src/io/openems/impl/device/commercial/FaultCharger.java index 34358945692..e4b71f2a8bd 100644 --- a/edge/src/io/openems/impl/device/commercial/FaultCharger.java +++ b/edge/src/io/openems/impl/device/commercial/FaultCharger.java @@ -1,88 +1,367 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultCharger implements FaultEnum{ - HighVoltageSideOfDCConverterUndervoltage(0), HighVoltageSideOfDCConverterOvervoltage(1), LowVoltageSideOfDCConverterUndervoltage(2), - LowVoltageSideOfDCConverterOvervoltage(3), HighVoltageSideOfDCConverterOvercurrentFault(4), LowVoltageSideOfDCConverterOvercurrentFault(5), DCConverterIGBTFault(6), DCConverterPrechargeUnmet(7), - BECUCommunicationDisconnected(8), DCConverterCommunicationDisconnected(9), CurrentConfigurationOverRange(10), TheBatteryRequestStop(11), OvercurrentRelayFault(12), LightningProtectionDeviceFault(13), - DCConverterPriamaryContactorDisconnectedAbnormally(14), DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(15), DCConvetorEEPROMAbnormity1(16), DCConvetorEEPROMAbnormity1Second(17), - EDCConvetorEEPROMAbnormity1(18), DCConvertorGeneralOverload(19), DCShortCircuit(20), PeakPulseCurrentProtection(21), DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(22), EffectivePulseValueOverhigh(23), - DCConverteSevereOverload(24), DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(25), DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(26), DCConvetorPrechargeContactorCloseFailed(27), - DCConvetorMainContactorCloseFailed(28), ACContactorStateAbnormityOfDCConvetor(29), DCConvetorEmergencyStop(30), DCConverterChargingGunDisconnected(31), DCCurrentAbnormityBeforeDCConvetorWork(32), FuSeDisconnected(33), - DCConverterHardwareCurrentOrVoltageFault(34), DCConverterCrystalOscillatorCircuitInvalidation(35), DCConverterResetCircuitInvalidation(36), DCConverterSamplingCircuitInvalidation(37), - DCConverterDigitalIOCircuitInvalidation(38), DCConverterPWMCircuitInvalidation(39), DCConverterX5045CircuitInvalidation(40), DCConverterCANCircuitInvalidation(41), DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(42), - DCConverterPowerCircuitInvalidation(43), DCConverterCPUInvalidation(44), DCConverterTINT0InterruptInvalidation(45), DCConverterADCInterruptInvalidation(46), DCConverterCAPITN4InterruptInvalidation(47), - DCConverterCAPINT6InterruptInvalidation(48), DCConverterT3PINTinterruptInvalidation(49), DCConverterT4PINTinterruptInvalidation(50), DCConverterPDPINTAInterruptInvalidation(51), DCConverterT1PINTInterruptInvalidation(52), - DCConverterRESVInterruptInvalidation(53), DCConverter100usTaskInvalidation(54), DCConverterClockInvalidation(55), DCConverterEMSMemoryInvalidation(56), DCConverterExteriorCommunicationInvalidation(57), - DCConverterIOInterfaceInvalidation(58), DCConverterInputVoltageBoundFault(59), DCConverterOutterVoltageBoundFault(60), DCConverterOutputVoltageBoundFault(61), DCConverterInductCurrentBoundFault(62), - DCConverterInputCurrentBoundFault(63), DCConverterOutputCurrentBoundFault(64), DCReactorOverTemperature(65), DCIGBTOverTemperature(66), DCConverterChanel3OverTemperature(67), DCConverterChanel4OverTemperature(68), - DCConverterChanel5OverTemperature(69), DCConverterChanel6OverTemperature(70), DCConverterChanel7OverTemperature(71), DCConverterChanel8OverTemperature(72), DCReactorTemperatureSamplingInvalidation(73), DCIGBTTemperatureSamplingInvalidation(74), - DCConverterChanel3TemperatureSamplingInvalidation(75), DCConverterChanel4TemperatureSamplingInvalidation(76), DCConverterChanel5TemperatureSamplingInvalidation(77), DCConverterChanel6TemperatureSamplingInvalidation(78), - DCConverterChanel7TemperatureSamplingInvalidation(79), DCConverterChanel8TemperatureSamplingInvalidation(80), DCConverterInductanceCurrentSamplingInvalidation(81), CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(82), - VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(83), InsulationInspectionFault(84), NegContactorCloseUnsuccessly(85), NegContactorCutWhenRunning(86), BmsDCDC1HighVoltageSideOfDCConverterUndervoltage(87), - BmsDCDC1HighVoltageSideOfDCConverterOvervoltage(88), BmsDCDC1LowVoltageSideOfDCConverterUndervoltage(89), BmsDCDC1LowVoltageSideOfDCConverterOvervoltage(90), BmsDCDC1HighVoltageSideOfDCConverterOvercurrentFault(91), - BmsDCDC1LowVoltageSideOfDCConverterOvercurrentFault(92), BmsDCDC1DCConverterIGBTFault(93), BmsDCDC1DCConverterPrechargeUnmet(94), BmsDCDC1BECUCommunicationDisconnected(95), BmsDCDC1DCConverterCommunicationDisconnected(96), - BmsDCDC1CurrentConfigurationOverRange(97), BmsDCDC1TheBatteryRequestStop(98), BmsDCDC1OvercurrentRelayFault(99), BmsDCDC1LightningProtectionDeviceFault(100), BmsDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(101), - BmsDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(102), BmsDCDC1DCConvetorEEPROMAbnormity1(103), BmsDCDC1DCConvetorEEPROMAbnormity1Second(104), BmsDCDC1EDCConvetorEEPROMAbnormity1(105), BsmDCDC1DCConvertorGeneralOverload(106), - BsmDCDC1DCShortCircuit(107), BsmDCDC1PeakPulseCurrentProtection(108), BsmDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(109), BsmDCDC1EffectivePulseValueOverhigh(110), BsmDCDC1DCConverteSevereOverload(111), - BsmDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(112), BsmDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(113), BsmDCDC1DCConvetorPrechargeContactorCloseFailed(114), BsmDCDC1DCConvetorMainContactorCloseFailed(115), - BsmDCDC1ACContactorStateAbnormityOfDCConvetor(116), BsmDCDC1DCConvetorEmergencyStop(117), BsmDCDC1DCConverterChargingGunDisconnected(118), BsmDCDC1DCCurrentAbnormityBeforeDCConvetorWork(119), BsmDCDC1FuSeDisconnected(120), - BsmDCDC1DCConverterHardwareCurrentOrVoltageFault(121), BmsDCDC1DCConverterCrystalOscillatorCircuitInvalidation(122), BmsDCDC1DCConverterResetCircuitInvalidation(123), BmsDCDC1DCConverterSamplingCircuitInvalidation(124), - BmsDCDC1DCConverterDigitalIOCircuitInvalidation(125), BmsDCDC1DCConverterPWMCircuitInvalidation(126), BmsDCDC1DCConverterX5045CircuitInvalidation(127), BmsDCDC1DCConverterCANCircuitInvalidation(128), - BmsDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(129), BmsDCDC1DCConverterPowerCircuitInvalidation(130), BmsDCDC1DCConverterCPUInvalidation(131), BmsDCDC1DCConverterTINT0InterruptInvalidation(132), - BmsDCDC1DCConverterADCInterruptInvalidation(133), BmsDCDC1DCConverterCAPITN4InterruptInvalidation(134), BmsDCDC1DCConverterCAPINT6InterruptInvalidation(135), BmsDCDC1DCConverterT3PINTinterruptInvalidation(136), - BmsDCDC1DCConverterT4PINTinterruptInvalidation(137), BmsDCDC1DCConverterPDPINTAInterruptInvalidation(138), BmsDCDC1DCConverterT1PINTInterruptInvalidationSecond(139), BmsDCDC1DCConverterRESVInterruptInvalidation(140), - BmsDCDC1DCConverter100usTaskInvalidation(141), BmsDCDC1DCConverterClockInvalidation(142), BmsDCDC1DCConverterEMSMemoryInvalidation(143), BmsDCDC1DCConverterExteriorCommunicationInvalidation(144), BmsDCDC1DCConverterIOInterfaceInvalidation(145), - BmsDCDC1DCConverterInputVoltageBoundFault(146), BmsDCDC1DCConverterOutterVoltageBoundFault(147), BmsDCDC1DCConverterOutputVoltageBoundFault(148), BmsDCDC1DCConverterInductCurrentBoundFault(149), BmsDCDC1DCConverterInputCurrentBoundFault(150), - BmsDCDC1DCConverterOutputCurrentBoundFault(151), BmsDCDC1DCReactorOverTemperature(152), BmsDCDC1DCIGBTOverTemperature(153), BmsDCDC1DCConverterChanel3OverTemperature(154), BmsDCDC1DCConverterChanel4OverTemperature(155), - BmsDCDC1DCConverterChanel5OverTemperature(156), BmsDCDC1DCConverterChanel6OverTemperature(157), BmsDCDC1DCConverterChanel7OverTemperature(158), BmsDCDC1DCConverterChanel8OverTemperature(159), BmsDCDC1DCReactorTemperatureSamplingInvalidation(160), - BmsDCDC1DCIGBTTemperatureSamplingInvalidation(161), BmsDCDC1DCConverterChanel3TemperatureSamplingInvalidation(162), BmsDCDC1DCConverterChanel4TemperatureSamplingInvalidation(163), BmsDCDC1DCConverterChanel5TemperatureSamplingInvalidation(164), - BmsDCDC1DCConverterChanel6TemperatureSamplingInvalidation(165), BmsDCDC1DCConverterChanel7TemperatureSamplingInvalidation(166), BmsDCDC1DCConverterChanel8TemperatureSamplingInvalidation(167), BmsDCDC1DCConverterInductanceCurrentSamplingInvalidation(168), - BmsDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(169), BmsDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(170), BmsDCDC1InsulationInspectionFault(171), BmsDCDC1NegContactorCloseUnsuccessly(172), - BmsDCDC1NegContactorCutWhenRunning(173), PvDCDCHighVoltageSideOfDCConverterUndervoltage(174), PvDCDCHighVoltageSideOfDCConverterOvervoltage(175), PvDCDCLowVoltageSideOfDCConverterUndervoltage(176), PvDCDCLowVoltageSideOfDCConverterOvervoltage(177), - PvDCDCHighVoltageSideOfDCConverterOvercurrentFault(178), PvDCDCLowVoltageSideOfDCConverterOvercurrentFault(179), PvDCDCDCConverterIGBTFault(180), PvDCDCDCConverterPrechargeUnmet(181), PvDCDCBECUCommunicationDisconnected(182), - PvDCDCDCConverterCommunicationDisconnected(183), PvDCDCCurrentConfigurationOverRange(184), PvDCDCTheBatteryRequestStop(185), PvDCDCOvercurrentRelayFault(186), PvDCDCLightningProtectionDeviceFault(187), - PvDCDCDCConverterPriamaryContactorDisconnectedAbnormally(188), PvDCDCDCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(189), PvDCDCDCConvetorEEPROMAbnormity1(190), PvDCDCDCConvetorEEPROMAbnormity1Second(191), PvDCDCEDCConvetorEEPROMAbnormity1(192), - PvDCDCDCConvertorGeneralOverload(193), PvDCDCDCShortCircuit(194), PvDCDCPeakPulseCurrentProtection(195), PvDCDCDCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(196), PvDCDCEffectivePulseValueOverhigh(197), PvDCDCDCConverteSevereOverload(198), - PvDCDCDCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(199), PvDCDCDCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(200), PvDCDCDCConvetorPrechargeContactorCloseFailed(201), PvDCDCDCConvetorMainContactorCloseFailed(202), - PvDCDCACContactorStateAbnormityOfDCConvetor(203), PvDCDCDCConvetorEmergencyStop(204), PvDCDCDCConverterChargingGunDisconnected(205), PvDCDCDCCurrentAbnormityBeforeDCConvetorWork(206), PvDCDCFuSeDisconnected(207), PvDCDCDCConverterHardwareCurrentOrVoltageFault(208), - PvDCDCDCConverterCrystalOscillatorCircuitInvalidation(209), PvDCDCDCConverterResetCircuitInvalidation(210), PvDCDCDCConverterSamplingCircuitInvalidation(211), PvDCDCDCConverterDigitalIOCircuitInvalidation(212), PvDCDCDCConverterPWMCircuitInvalidation(213), - PvDCDCDCConverterX5045CircuitInvalidation(214), PvDCDCDCConverterCANCircuitInvalidation(215), PvDCDCDCConverterSoftwareANDHardwareProtectionCircuitInvalidation(216), PvDCDCDCConverterPowerCircuitInvalidation(217), PvDCDCDCConverterCPUInvalidation(218), - PvDCDCDCConverterTINT0InterruptInvalidation(219), PvDCDCDCConverterADCInterruptInvalidation(220), PvDCDCDCConverterCAPITN4InterruptInvalidation(221), PvDCDCDCConverterCAPINT6InterruptInvalidation(222), PvDCDCDCConverterT3PINTinterruptInvalidation(223), - PvDCDCDCConverterT4PINTinterruptInvalidation(224), PvDCDCConverterPDPINTAInterruptInvalidation(225), PvDCDCConverterT1PINTInterruptInvalidationSecond(226), PvDCDCConverterRESVInterruptInvalidation(227), PvDCDCConverter100usTaskInvalidation(228), - PvDCDCConverterClockInvalidation(229), PvDCDCConverterEMSMemoryInvalidation(230), PvDCDCConverterExteriorCommunicationInvalidation(231), PvDCDCConverterIOInterfaceInvalidation(232), PvDCDCConverterInputVoltageBoundFault(233), - PvDCDCConverterOutterVoltageBoundFault(234), PvDCDCConverterOutputVoltageBoundFault(235), PvDCDCConverterInductCurrentBoundFault(236), PvDCDCConverterInputCurrentBoundFault(237), PvDCDCConverterOutputCurrentBoundFault(238), PvDCDCDCReactorOverTemperature(239), - PvDCDCDCIGBTOverTemperature(240), PvDCDCDCConverterChanel3OverTemperature(241), PvDCDCDCConverterChanel4OverTemperature(242), PvDCDCDCConverterChanel5OverTemperature(243), PvDCDCDCConverterChanel6OverTemperature(244), - PvDCDCDCConverterChanel7OverTemperature(245), PvDCDCDCConverterChanel8OverTemperature(246), PvDCDCDCReactorTemperatureSamplingInvalidation(247), PvDCDCDCIGBTTemperatureSamplingInvalidation(248), PvDCDCDCConverterChanel3TemperatureSamplingInvalidation(249), - PvDCDCDCConverterChanel4TemperatureSamplingInvalidation(250), PvDCDCDCConverterChanel5TemperatureSamplingInvalidation(251), PvDCDCDCConverterChanel6TemperatureSamplingInvalidation(252), PvDCDCDCConverterChanel7TemperatureSamplingInvalidation(253), - PvDCDCDCConverterChanel8TemperatureSamplingInvalidation(254), PvDCDCDCConverterInductanceCurrentSamplingInvalidation(255), PvDCDCCurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(256), PvDCDCVoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(257), - PvDCDCInsulationInspectionFault(258), PvDCDCNegContactorCloseUnsuccessly(259), PvDCDCNegContactorCutWhenRunning(260), PvDCDC1HighVoltageSideOfDCConverterUndervoltage(261), PvDCDC1HighVoltageSideOfDCConverterOvervoltage(262), PvDCDC1LowVoltageSideOfDCConverterUndervoltage(263), - PvDCDC1LowVoltageSideOfDCConverterOvervoltage(264), PvDCDC1HighVoltageSideOfDCConverterOvercurrentFault(265), PvDCDC1LowVoltageSideOfDCConverterOvercurrentFault(266), PvDCDC1DCConverterIGBTFault(267), PvDCDC1DCConverterPrechargeUnmet(268), PvDCDC1BECUCommunicationDisconnected(269), - PvDCDC1DCConverterCommunicationDisconnected(270), PvDCDC1CurrentConfigurationOverRange(271), PvDCDC1TheBatteryRequestStop(272), PvDCDC1OvercurrentRelayFault(273), PvDCDC1LightningProtectionDeviceFault(274), PvDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(275), - PvDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(276), PvDCDC1DCConvetorEEPROMAbnormity1(277), PvDCDC1DCConvetorEEPROMAbnormity1Second(278), PvDCDC1EDCConvetorEEPROMAbnormity1(279), PvDCDC1DCConvertorGeneralOverload(280), PvDCDC1DCShortCircuit(281), - PvDCDC1PeakPulseCurrentProtection(282), PvDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(283), PvDCDC1EffectivePulseValueOverhigh(284), PvDCDC1DCConverteSevereOverload(285), PvDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(286), - PvDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(287), PvDCDC1DCConvetorPrechargeContactorCloseFailed(288), PvDCDC1DCConvetorMainContactorCloseFailed(289), PvDCDC1ACContactorStateAbnormityOfDCConvetor(290), PvDCDC1DCConvetorEmergencyStop(291), - PvDCDC1DCConverterChargingGunDisconnected(292), PvDCDC1DCCurrentAbnormityBeforeDCConvetorWork(293), PvDCDC1FuSeDisconnected(294), PvDCDC1DCConverterHardwareCurrentOrVoltageFault(295), PvDCDC1DCConverterCrystalOscillatorCircuitInvalidation(296), PvDCDC1DCConverterResetCircuitInvalidation(297), - PvDCDC1DCConverterSamplingCircuitInvalidation(298), PvDCDC1DCConverterDigitalIOCircuitInvalidation(299), PvDCDC1DCConverterPWMCircuitInvalidation(300), PvDCDC1DCConverterX5045CircuitInvalidation(301), PvDCDC1DCConverterCANCircuitInvalidation(302), - PvDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(303), PvDCDC1DCConverterPowerCircuitInvalidation(304), PvDCDC1DCConverterCPUInvalidation(305), PvDCDC1DCConverterTINT0InterruptInvalidation(306), PvDCDC1DCConverterADCInterruptInvalidation(307), - PvDCDC1DCConverterCAPITN4InterruptInvalidation(308), PvDCDC1DCConverterCAPINT6InterruptInvalidation(309), PvDCDC1DCConverterT3PINTinterruptInvalidation(310), PvDCDC1DCConverterT4PINTinterruptInvalidation(311), PvDCDC1DCConverterPDPINTAInterruptInvalidation(312), - PvDCDC1DCConverterT1PINTInterruptInvalidationSecond(313), PvDCDC1DCConverterRESVInterruptInvalidation(314), PvDCDC1DCConverter100usTaskInvalidation(315), PvDCDC1DCConverterClockInvalidation(316), PvDCDC1DCConverterEMSMemoryInvalidation(317), - PvDCDC1DCConverterExteriorCommunicationInvalidation(318), PvDCDC1DCConverterIOInterfaceInvalidation(319), PvDCDC1DCConverterInputVoltageBoundFault(320), PvDCDC1DCConverterOutterVoltageBoundFault(321), PvDCDC1DCConverterOutputVoltageBoundFault(322), - PvDCDC1DCConverterInductCurrentBoundFault(323), PvDCDC1DCConverterInputCurrentBoundFault(324), PvDCDC1DCConverterOutputCurrentBoundFault(325), PvDCDC1DCReactorOverTemperature(326), PvDCDC1DCIGBTOverTemperature(327), PvDCDC1DCConverterChanel3OverTemperature(328), - PvDCDC1DCConverterChanel4OverTemperature(329), PvDCDC1DCConverterChanel5OverTemperature(330), PvDCDC1DCConverterChanel6OverTemperature(331), PvDCDC1DCConverterChanel7OverTemperature(332), PvDCDC1DCConverterChanel8OverTemperature(333), PvDCDC1DCReactorTemperatureSamplingInvalidation(334), - PvDCDC1DCIGBTTemperatureSamplingInvalidation(335), PvDCDC1DCConverterChanel3TemperatureSamplingInvalidation(336), PvDCDC1DCConverterChanel4TemperatureSamplingInvalidation(337), PvDCDC1DCConverterChanel5TemperatureSamplingInvalidation(338), PvDCDC1DCConverterChanel6TemperatureSamplingInvalidation(339), - PvDCDC1DCConverterChanel7TemperatureSamplingInvalidation(340), PvDCDC1DCConverterChanel8TemperatureSamplingInvalidation(341), PvDCDC1DCConverterInductanceCurrentSamplingInvalidation(342), PvDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(343), - PvDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(344), PvDCDC1InsulationInspectionFault(345), PvDCDC1NegContactorCloseUnsuccessly(346), PvDCDC1NegContactorCutWhenRunning(347); - - private final int value; - - private FaultCharger(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconCommercialCharger.class) +public enum FaultCharger implements FaultEnum { + HighVoltageSideOfDCConverterUndervoltage(0), // + HighVoltageSideOfDCConverterOvervoltage(1), // + LowVoltageSideOfDCConverterUndervoltage(2), // + LowVoltageSideOfDCConverterOvervoltage(3), // + HighVoltageSideOfDCConverterOvercurrentFault(4), // + LowVoltageSideOfDCConverterOvercurrentFault(5), // + DCConverterIGBTFault(6), // + DCConverterPrechargeUnmet(7), // + BECUCommunicationDisconnected(8), // + DCConverterCommunicationDisconnected(9), // + CurrentConfigurationOverRange(10), // + TheBatteryRequestStop(11), // + OvercurrentRelayFault(12), // + LightningProtectionDeviceFault(13), // + DCConverterPriamaryContactorDisconnectedAbnormally(14), // + DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(15), // + DCConvetorEEPROMAbnormity1(16), // + DCConvetorEEPROMAbnormity1Second(17), // + EDCConvetorEEPROMAbnormity1(18), // + DCConvertorGeneralOverload(19), // + DCShortCircuit(20), // + PeakPulseCurrentProtection(21), // + DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(22), // + EffectivePulseValueOverhigh(23), // + DCConverteSevereOverload(24), // + DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(25), // + DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(26), // + DCConvetorPrechargeContactorCloseFailed(27), // + DCConvetorMainContactorCloseFailed(28), // + ACContactorStateAbnormityOfDCConvetor(29), // + DCConvetorEmergencyStop(30), // + DCConverterChargingGunDisconnected(31), // + DCCurrentAbnormityBeforeDCConvetorWork(32), // + FuSeDisconnected(33), // + DCConverterHardwareCurrentOrVoltageFault(34), // + DCConverterCrystalOscillatorCircuitInvalidation(35), // + DCConverterResetCircuitInvalidation(36), // + DCConverterSamplingCircuitInvalidation(37), // + DCConverterDigitalIOCircuitInvalidation(38), // + DCConverterPWMCircuitInvalidation(39), // + DCConverterX5045CircuitInvalidation(40), // + DCConverterCANCircuitInvalidation(41), // + DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(42), // + DCConverterPowerCircuitInvalidation(43), // + DCConverterCPUInvalidation(44), // + DCConverterTINT0InterruptInvalidation(45), // + DCConverterADCInterruptInvalidation(46), // + DCConverterCAPITN4InterruptInvalidation(47), // + DCConverterCAPINT6InterruptInvalidation(48), // + DCConverterT3PINTinterruptInvalidation(49), // + DCConverterT4PINTinterruptInvalidation(50), // + DCConverterPDPINTAInterruptInvalidation(51), // + DCConverterT1PINTInterruptInvalidation(52), // + DCConverterRESVInterruptInvalidation(53), // + DCConverter100usTaskInvalidation(54), // + DCConverterClockInvalidation(55), // + DCConverterEMSMemoryInvalidation(56), // + DCConverterExteriorCommunicationInvalidation(57), // + DCConverterIOInterfaceInvalidation(58), // + DCConverterInputVoltageBoundFault(59), // + DCConverterOutterVoltageBoundFault(60), // + DCConverterOutputVoltageBoundFault(61), // + DCConverterInductCurrentBoundFault(62), // + DCConverterInputCurrentBoundFault(63), // + DCConverterOutputCurrentBoundFault(64), // + DCReactorOverTemperature(65), // + DCIGBTOverTemperature(66), // + DCConverterChanel3OverTemperature(67), // + DCConverterChanel4OverTemperature(68), // + DCConverterChanel5OverTemperature(69), // + DCConverterChanel6OverTemperature(70), // + DCConverterChanel7OverTemperature(71), // + DCConverterChanel8OverTemperature(72), // + DCReactorTemperatureSamplingInvalidation(73), // + DCIGBTTemperatureSamplingInvalidation(74), // + DCConverterChanel3TemperatureSamplingInvalidation(75), // + DCConverterChanel4TemperatureSamplingInvalidation(76), // + DCConverterChanel5TemperatureSamplingInvalidation(77), // + DCConverterChanel6TemperatureSamplingInvalidation(78), // + DCConverterChanel7TemperatureSamplingInvalidation(79), // + DCConverterChanel8TemperatureSamplingInvalidation(80), // + DCConverterInductanceCurrentSamplingInvalidation(81), // + CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(82), // + VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(83), // + InsulationInspectionFault(84), // + NegContactorCloseUnsuccessly(85), // + NegContactorCutWhenRunning(86), // + BmsDCDC1HighVoltageSideOfDCConverterUndervoltage(87), // + BmsDCDC1HighVoltageSideOfDCConverterOvervoltage(88), // + BmsDCDC1LowVoltageSideOfDCConverterUndervoltage(89), // + BmsDCDC1LowVoltageSideOfDCConverterOvervoltage(90), // + BmsDCDC1HighVoltageSideOfDCConverterOvercurrentFault(91), // + BmsDCDC1LowVoltageSideOfDCConverterOvercurrentFault(92), // + BmsDCDC1DCConverterIGBTFault(93), // + BmsDCDC1DCConverterPrechargeUnmet(94), // + BmsDCDC1BECUCommunicationDisconnected(95), // + BmsDCDC1DCConverterCommunicationDisconnected(96), // + BmsDCDC1CurrentConfigurationOverRange(97), // + BmsDCDC1TheBatteryRequestStop(98), // + BmsDCDC1OvercurrentRelayFault(99), // + BmsDCDC1LightningProtectionDeviceFault(100), // + BmsDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(101), // + BmsDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(102), // + BmsDCDC1DCConvetorEEPROMAbnormity1(103), // + BmsDCDC1DCConvetorEEPROMAbnormity1Second(104), // + BmsDCDC1EDCConvetorEEPROMAbnormity1(105), // + BsmDCDC1DCConvertorGeneralOverload(106), // + BsmDCDC1DCShortCircuit(107), // + BsmDCDC1PeakPulseCurrentProtection(108), // + BsmDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(109), // + BsmDCDC1EffectivePulseValueOverhigh(110), // + BsmDCDC1DCConverteSevereOverload(111), // + BsmDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(112), // + BsmDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(113), // + BsmDCDC1DCConvetorPrechargeContactorCloseFailed(114), // + BsmDCDC1DCConvetorMainContactorCloseFailed(115), // + BsmDCDC1ACContactorStateAbnormityOfDCConvetor(116), // + BsmDCDC1DCConvetorEmergencyStop(117), // + BsmDCDC1DCConverterChargingGunDisconnected(118), // + BsmDCDC1DCCurrentAbnormityBeforeDCConvetorWork(119), // + BsmDCDC1FuSeDisconnected(120), // + BsmDCDC1DCConverterHardwareCurrentOrVoltageFault(121), // + BmsDCDC1DCConverterCrystalOscillatorCircuitInvalidation(122), // + BmsDCDC1DCConverterResetCircuitInvalidation(123), // + BmsDCDC1DCConverterSamplingCircuitInvalidation(124), // + BmsDCDC1DCConverterDigitalIOCircuitInvalidation(125), // + BmsDCDC1DCConverterPWMCircuitInvalidation(126), // + BmsDCDC1DCConverterX5045CircuitInvalidation(127), // + BmsDCDC1DCConverterCANCircuitInvalidation(128), // + BmsDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(129), // + BmsDCDC1DCConverterPowerCircuitInvalidation(130), // + BmsDCDC1DCConverterCPUInvalidation(131), // + BmsDCDC1DCConverterTINT0InterruptInvalidation(132), // + BmsDCDC1DCConverterADCInterruptInvalidation(133), // + BmsDCDC1DCConverterCAPITN4InterruptInvalidation(134), // + BmsDCDC1DCConverterCAPINT6InterruptInvalidation(135), // + BmsDCDC1DCConverterT3PINTinterruptInvalidation(136), // + BmsDCDC1DCConverterT4PINTinterruptInvalidation(137), // + BmsDCDC1DCConverterPDPINTAInterruptInvalidation(138), // + BmsDCDC1DCConverterT1PINTInterruptInvalidationSecond(139), // + BmsDCDC1DCConverterRESVInterruptInvalidation(140), // + BmsDCDC1DCConverter100usTaskInvalidation(141), // + BmsDCDC1DCConverterClockInvalidation(142), // + BmsDCDC1DCConverterEMSMemoryInvalidation(143), // + BmsDCDC1DCConverterExteriorCommunicationInvalidation(144), // + BmsDCDC1DCConverterIOInterfaceInvalidation(145), // + BmsDCDC1DCConverterInputVoltageBoundFault(146), // + BmsDCDC1DCConverterOutterVoltageBoundFault(147), // + BmsDCDC1DCConverterOutputVoltageBoundFault(148), // + BmsDCDC1DCConverterInductCurrentBoundFault(149), // + BmsDCDC1DCConverterInputCurrentBoundFault(150), // + BmsDCDC1DCConverterOutputCurrentBoundFault(151), // + BmsDCDC1DCReactorOverTemperature(152), // + BmsDCDC1DCIGBTOverTemperature(153), // + BmsDCDC1DCConverterChanel3OverTemperature(154), // + BmsDCDC1DCConverterChanel4OverTemperature(155), // + BmsDCDC1DCConverterChanel5OverTemperature(156), // + BmsDCDC1DCConverterChanel6OverTemperature(157), // + BmsDCDC1DCConverterChanel7OverTemperature(158), // + BmsDCDC1DCConverterChanel8OverTemperature(159), // + BmsDCDC1DCReactorTemperatureSamplingInvalidation(160), // + BmsDCDC1DCIGBTTemperatureSamplingInvalidation(161), // + BmsDCDC1DCConverterChanel3TemperatureSamplingInvalidation(162), // + BmsDCDC1DCConverterChanel4TemperatureSamplingInvalidation(163), // + BmsDCDC1DCConverterChanel5TemperatureSamplingInvalidation(164), // + BmsDCDC1DCConverterChanel6TemperatureSamplingInvalidation(165), // + BmsDCDC1DCConverterChanel7TemperatureSamplingInvalidation(166), // + BmsDCDC1DCConverterChanel8TemperatureSamplingInvalidation(167), // + BmsDCDC1DCConverterInductanceCurrentSamplingInvalidation(168), // + BmsDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(169), // + BmsDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(170), // + BmsDCDC1InsulationInspectionFault(171), // + BmsDCDC1NegContactorCloseUnsuccessly(172), // + BmsDCDC1NegContactorCutWhenRunning(173), // + PvDCDCHighVoltageSideOfDCConverterUndervoltage(174), // + PvDCDCHighVoltageSideOfDCConverterOvervoltage(175), // + PvDCDCLowVoltageSideOfDCConverterUndervoltage(176), // + PvDCDCLowVoltageSideOfDCConverterOvervoltage(177), // + PvDCDCHighVoltageSideOfDCConverterOvercurrentFault(178), // + PvDCDCLowVoltageSideOfDCConverterOvercurrentFault(179), // + PvDCDCDCConverterIGBTFault(180), // + PvDCDCDCConverterPrechargeUnmet(181), // + PvDCDCBECUCommunicationDisconnected(182), // + PvDCDCDCConverterCommunicationDisconnected(183), // + PvDCDCCurrentConfigurationOverRange(184), // + PvDCDCTheBatteryRequestStop(185), // + PvDCDCOvercurrentRelayFault(186), // + PvDCDCLightningProtectionDeviceFault(187), // + PvDCDCDCConverterPriamaryContactorDisconnectedAbnormally(188), // + PvDCDCDCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(189), // + PvDCDCDCConvetorEEPROMAbnormity1(190), // + PvDCDCDCConvetorEEPROMAbnormity1Second(191), // + PvDCDCEDCConvetorEEPROMAbnormity1(192), // + PvDCDCDCConvertorGeneralOverload(193), // + PvDCDCDCShortCircuit(194), // + PvDCDCPeakPulseCurrentProtection(195), // + PvDCDCDCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(196), // + PvDCDCEffectivePulseValueOverhigh(197), // + PvDCDCDCConverteSevereOverload(198), // + PvDCDCDCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(199), // + PvDCDCDCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(200), // + PvDCDCDCConvetorPrechargeContactorCloseFailed(201), // + PvDCDCDCConvetorMainContactorCloseFailed(202), // + PvDCDCACContactorStateAbnormityOfDCConvetor(203), // + PvDCDCDCConvetorEmergencyStop(204), // + PvDCDCDCConverterChargingGunDisconnected(205), // + PvDCDCDCCurrentAbnormityBeforeDCConvetorWork(206), // + PvDCDCFuSeDisconnected(207), // + PvDCDCDCConverterHardwareCurrentOrVoltageFault(208), // + PvDCDCDCConverterCrystalOscillatorCircuitInvalidation(209), // + PvDCDCDCConverterResetCircuitInvalidation(210), // + PvDCDCDCConverterSamplingCircuitInvalidation(211), // + PvDCDCDCConverterDigitalIOCircuitInvalidation(212), // + PvDCDCDCConverterPWMCircuitInvalidation(213), // + PvDCDCDCConverterX5045CircuitInvalidation(214), // + PvDCDCDCConverterCANCircuitInvalidation(215), // + PvDCDCDCConverterSoftwareANDHardwareProtectionCircuitInvalidation(216), // + PvDCDCDCConverterPowerCircuitInvalidation(217), // + PvDCDCDCConverterCPUInvalidation(218), // + PvDCDCDCConverterTINT0InterruptInvalidation(219), // + PvDCDCDCConverterADCInterruptInvalidation(220), // + PvDCDCDCConverterCAPITN4InterruptInvalidation(221), // + PvDCDCDCConverterCAPINT6InterruptInvalidation(222), // + PvDCDCDCConverterT3PINTinterruptInvalidation(223), // + PvDCDCDCConverterT4PINTinterruptInvalidation(224), // + PvDCDCConverterPDPINTAInterruptInvalidation(225), // + PvDCDCConverterT1PINTInterruptInvalidationSecond(226), // + PvDCDCConverterRESVInterruptInvalidation(227), // + PvDCDCConverter100usTaskInvalidation(228), // + PvDCDCConverterClockInvalidation(229), // + PvDCDCConverterEMSMemoryInvalidation(230), // + PvDCDCConverterExteriorCommunicationInvalidation(231), // + PvDCDCConverterIOInterfaceInvalidation(232), // + PvDCDCConverterInputVoltageBoundFault(233), // + PvDCDCConverterOutterVoltageBoundFault(234), // + PvDCDCConverterOutputVoltageBoundFault(235), // + PvDCDCConverterInductCurrentBoundFault(236), // + PvDCDCConverterInputCurrentBoundFault(237), // + PvDCDCConverterOutputCurrentBoundFault(238), // + PvDCDCDCReactorOverTemperature(239), // + PvDCDCDCIGBTOverTemperature(240), // + PvDCDCDCConverterChanel3OverTemperature(241), // + PvDCDCDCConverterChanel4OverTemperature(242), // + PvDCDCDCConverterChanel5OverTemperature(243), // + PvDCDCDCConverterChanel6OverTemperature(244), // + PvDCDCDCConverterChanel7OverTemperature(245), // + PvDCDCDCConverterChanel8OverTemperature(246), // + PvDCDCDCReactorTemperatureSamplingInvalidation(247), // + PvDCDCDCIGBTTemperatureSamplingInvalidation(248), // + PvDCDCDCConverterChanel3TemperatureSamplingInvalidation(249), // + PvDCDCDCConverterChanel4TemperatureSamplingInvalidation(250), // + PvDCDCDCConverterChanel5TemperatureSamplingInvalidation(251), // + PvDCDCDCConverterChanel6TemperatureSamplingInvalidation(252), // + PvDCDCDCConverterChanel7TemperatureSamplingInvalidation(253), // + PvDCDCDCConverterChanel8TemperatureSamplingInvalidation(254), // + PvDCDCDCConverterInductanceCurrentSamplingInvalidation(255), // + PvDCDCCurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(256), // + PvDCDCVoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(257), // + PvDCDCInsulationInspectionFault(258), // + PvDCDCNegContactorCloseUnsuccessly(259), // + PvDCDCNegContactorCutWhenRunning(260), // + PvDCDC1HighVoltageSideOfDCConverterUndervoltage(261), // + PvDCDC1HighVoltageSideOfDCConverterOvervoltage(262), // + PvDCDC1LowVoltageSideOfDCConverterUndervoltage(263), // + PvDCDC1LowVoltageSideOfDCConverterOvervoltage(264), // + PvDCDC1HighVoltageSideOfDCConverterOvercurrentFault(265), // + PvDCDC1LowVoltageSideOfDCConverterOvercurrentFault(266), // + PvDCDC1DCConverterIGBTFault(267), // + PvDCDC1DCConverterPrechargeUnmet(268), // + PvDCDC1BECUCommunicationDisconnected(269), // + PvDCDC1DCConverterCommunicationDisconnected(270), // + PvDCDC1CurrentConfigurationOverRange(271), // + PvDCDC1TheBatteryRequestStop(272), // + PvDCDC1OvercurrentRelayFault(273), // + PvDCDC1LightningProtectionDeviceFault(274), // + PvDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(275), // + PvDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(276), // + PvDCDC1DCConvetorEEPROMAbnormity1(277), // + PvDCDC1DCConvetorEEPROMAbnormity1Second(278), // + PvDCDC1EDCConvetorEEPROMAbnormity1(279), // + PvDCDC1DCConvertorGeneralOverload(280), // + PvDCDC1DCShortCircuit(281), // + PvDCDC1PeakPulseCurrentProtection(282), // + PvDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(283), // + PvDCDC1EffectivePulseValueOverhigh(284), // + PvDCDC1DCConverteSevereOverload(285), // + PvDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(286), // + PvDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(287), // + PvDCDC1DCConvetorPrechargeContactorCloseFailed(288), // + PvDCDC1DCConvetorMainContactorCloseFailed(289), // + PvDCDC1ACContactorStateAbnormityOfDCConvetor(290), // + PvDCDC1DCConvetorEmergencyStop(291), // + PvDCDC1DCConverterChargingGunDisconnected(292), // + PvDCDC1DCCurrentAbnormityBeforeDCConvetorWork(293), // + PvDCDC1FuSeDisconnected(294), // + PvDCDC1DCConverterHardwareCurrentOrVoltageFault(295), // + PvDCDC1DCConverterCrystalOscillatorCircuitInvalidation(296), // + PvDCDC1DCConverterResetCircuitInvalidation(297), // + PvDCDC1DCConverterSamplingCircuitInvalidation(298), // + PvDCDC1DCConverterDigitalIOCircuitInvalidation(299), // + PvDCDC1DCConverterPWMCircuitInvalidation(300), // + PvDCDC1DCConverterX5045CircuitInvalidation(301), // + PvDCDC1DCConverterCANCircuitInvalidation(302), // + PvDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(303), // + PvDCDC1DCConverterPowerCircuitInvalidation(304), // + PvDCDC1DCConverterCPUInvalidation(305), // + PvDCDC1DCConverterTINT0InterruptInvalidation(306), // + PvDCDC1DCConverterADCInterruptInvalidation(307), // + PvDCDC1DCConverterCAPITN4InterruptInvalidation(308), // + PvDCDC1DCConverterCAPINT6InterruptInvalidation(309), // + PvDCDC1DCConverterT3PINTinterruptInvalidation(310), // + PvDCDC1DCConverterT4PINTinterruptInvalidation(311), // + PvDCDC1DCConverterPDPINTAInterruptInvalidation(312), // + PvDCDC1DCConverterT1PINTInterruptInvalidationSecond(313), // + PvDCDC1DCConverterRESVInterruptInvalidation(314), // + PvDCDC1DCConverter100usTaskInvalidation(315), // + PvDCDC1DCConverterClockInvalidation(316), // + PvDCDC1DCConverterEMSMemoryInvalidation(317), // + PvDCDC1DCConverterExteriorCommunicationInvalidation(318), // + PvDCDC1DCConverterIOInterfaceInvalidation(319), // + PvDCDC1DCConverterInputVoltageBoundFault(320), // + PvDCDC1DCConverterOutterVoltageBoundFault(321), // + PvDCDC1DCConverterOutputVoltageBoundFault(322), // + PvDCDC1DCConverterInductCurrentBoundFault(323), // + PvDCDC1DCConverterInputCurrentBoundFault(324), // + PvDCDC1DCConverterOutputCurrentBoundFault(325), // + PvDCDC1DCReactorOverTemperature(326), // + PvDCDC1DCIGBTOverTemperature(327), // + PvDCDC1DCConverterChanel3OverTemperature(328), // + PvDCDC1DCConverterChanel4OverTemperature(329), // + PvDCDC1DCConverterChanel5OverTemperature(330), // + PvDCDC1DCConverterChanel6OverTemperature(331), // + PvDCDC1DCConverterChanel7OverTemperature(332), // + PvDCDC1DCConverterChanel8OverTemperature(333), // + PvDCDC1DCReactorTemperatureSamplingInvalidation(334), // + PvDCDC1DCIGBTTemperatureSamplingInvalidation(335), // + PvDCDC1DCConverterChanel3TemperatureSamplingInvalidation(336), // + PvDCDC1DCConverterChanel4TemperatureSamplingInvalidation(337), // + PvDCDC1DCConverterChanel5TemperatureSamplingInvalidation(338), // + PvDCDC1DCConverterChanel6TemperatureSamplingInvalidation(339), // + PvDCDC1DCConverterChanel7TemperatureSamplingInvalidation(340), // + PvDCDC1DCConverterChanel8TemperatureSamplingInvalidation(341), // + PvDCDC1DCConverterInductanceCurrentSamplingInvalidation(342), // + PvDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(343), // + PvDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(344), // + PvDCDC1InsulationInspectionFault(345), // + PvDCDC1NegContactorCloseUnsuccessly(346), // + PvDCDC1NegContactorCutWhenRunning(347); + + private final int value; + + private FaultCharger(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/FaultEss.java b/edge/src/io/openems/impl/device/commercial/FaultEss.java index 0e8e7f15c90..50f83813fd9 100644 --- a/edge/src/io/openems/impl/device/commercial/FaultEss.java +++ b/edge/src/io/openems/impl/device/commercial/FaultEss.java @@ -1,35 +1,94 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum { - - DCPrechargeContactorCloseUnsuccessfully(0), ACPrechargeContactorCloseUnsuccessfully(1), ACMainContactorCloseUnsuccessfully(2), DCElectricalBreaker1CloseUnsuccessfully(3), - DCMainContactorCloseUnsuccessfully(4), ACBreakerTrip(5), ACMainContactorOpenWhenRunning(6), DCMainContactorOpenWhenRunning(7), ACMainContactorOpenUnsuccessfully(8), - DCElectricalBreaker1OpenUnsuccessfully(9), DCMainContactorOpenUnsuccessfully(10), HardwarePDPFault(11), MasterStopSuddenly(12), DCShortCircuitProtection(13), DCOvervoltageProtection(14), - DCUndervoltageProtection(15), DCInverseNoConnectionProtection(16), DCDisconnectionProtection(17), CommutingVoltageAbnormityProtection(18), DCOvercurrentProtection(19), - Phase1PeakCurrentOverLimitProtection(20), Phase2PeakCurrentOverLimitProtection(21), Phase3PeakCurrentOverLimitProtection(22), Phase1GridVoltageSamplingInvalidation(23), - Phase2VirtualCurrentOverLimitProtection(24), Phase3VirtualCurrentOverLimitProtection(25), Phase1GridVoltageSamplingInvalidation2(26), Phase2ridVoltageSamplingInvalidation(27), - Phase3GridVoltageSamplingInvalidation(28), Phase1InvertVoltageSamplingInvalidation(29), Phase2InvertVoltageSamplingInvalidation(30), Phase3InvertVoltageSamplingInvalidation(31), - ACCurrentSamplingInvalidation(32), DCCurrentSamplingInvalidation(33), Phase1OvertemperatureProtection(34), Phase2OvertemperatureProtection(35), Phase3OvertemperatureProtection(36), - Phase1TemperatureSamplingInvalidation(37), Phase2TemperatureSamplingInvalidation(38), Phase3TemperatureSamplingInvalidation(39), Phase1PrechargeUnmetProtection(40), Phase2PrechargeUnmetProtection(41), - Phase3PrechargeUnmetProtection(42), UnadaptablePhaseSequenceErrorProtection(43), DSPProtection(44), Phase1GridVoltageSevereOvervoltageProtection(45),Phase1GridVoltageGeneralOvervoltageProtection(46), - Phase2GridVoltageSevereOvervoltageProtection(47), Phase2GridVoltageGeneralOvervoltageProtection(48), Phase3GridVoltageSevereOvervoltageProtection(49), Phase3GridVoltageGeneralOvervoltageProtection(50), - Phase1GridVoltageSevereUndervoltageProtection(51), Phase1GridVoltageGeneralUndervoltageProtection(52), Phase2GridVoltageSevereUndervoltageProtection(53), Phase2GridVoltageGeneralUndervoltageProtection(54), - Phase3GridVoltageSevereUndervoltageProtection(55), Phase3GridVoltageGeneralUndervoltageProtection(56), SevereOverfrequncyProtection(57), GeneralOverfrequncyProtection(58), SevereUnderfrequncyProtection(59), - GeneralsUnderfrequncyProtection(60), Phase1Gridloss(61), Phase2Gridloss(62), Phase3Gridloss(63), IslandingProtection(64), Phase1UnderVoltageRideThrough(65), Phase2UnderVoltageRideThrough(66), - Phase3UnderVoltageRideThrough(67), Phase1InverterVoltageSevereOvervoltageProtection(68), Phase1InverterVoltageGeneralOvervoltageProtection(69), Phase2InverterVoltageSevereOvervoltageProtection(70), - Phase2InverterVoltageGeneralOvervoltageProtection(71), Phase3InverterVoltageSevereOvervoltageProtection(72), Phase3InverterVoltageGeneralOvervoltageProtection(73), - InverterPeakVoltageHighProtectionCauseByACDisconnect(74); - - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconCommercialEss.class) +public enum FaultEss implements FaultEnum { + DCPrechargeContactorCloseUnsuccessfully(0), // + ACPrechargeContactorCloseUnsuccessfully(1), // + ACMainContactorCloseUnsuccessfully(2), // + DCElectricalBreaker1CloseUnsuccessfully(3), // + DCMainContactorCloseUnsuccessfully(4), // + ACBreakerTrip(5), // + ACMainContactorOpenWhenRunning(6), // + DCMainContactorOpenWhenRunning(7), // + ACMainContactorOpenUnsuccessfully(8), // + DCElectricalBreaker1OpenUnsuccessfully(9), // + DCMainContactorOpenUnsuccessfully(10), // + HardwarePDPFault(11), // + MasterStopSuddenly(12), // + DCShortCircuitProtection(13), // + DCOvervoltageProtection(14), // + DCUndervoltageProtection(15), // + DCInverseNoConnectionProtection(16), // + DCDisconnectionProtection(17), // + CommutingVoltageAbnormityProtection(18), // + DCOvercurrentProtection(19), // + Phase1PeakCurrentOverLimitProtection(20), // + Phase2PeakCurrentOverLimitProtection(21), // + Phase3PeakCurrentOverLimitProtection(22), // + Phase1GridVoltageSamplingInvalidation(23), // + Phase2VirtualCurrentOverLimitProtection(24), // + Phase3VirtualCurrentOverLimitProtection(25), // + Phase1GridVoltageSamplingInvalidation2(26), // + Phase2ridVoltageSamplingInvalidation(27), // + Phase3GridVoltageSamplingInvalidation(28), // + Phase1InvertVoltageSamplingInvalidation(29), // + Phase2InvertVoltageSamplingInvalidation(30), // + Phase3InvertVoltageSamplingInvalidation(31), // + ACCurrentSamplingInvalidation(32), // + DCCurrentSamplingInvalidation(33), // + Phase1OvertemperatureProtection(34), // + Phase2OvertemperatureProtection(35), // + Phase3OvertemperatureProtection(36), // + Phase1TemperatureSamplingInvalidation(37), // + Phase2TemperatureSamplingInvalidation(38), // + Phase3TemperatureSamplingInvalidation(39), // + Phase1PrechargeUnmetProtection(40), // + Phase2PrechargeUnmetProtection(41), // + Phase3PrechargeUnmetProtection(42), // + UnadaptablePhaseSequenceErrorProtection(43), // + DSPProtection(44), // + Phase1GridVoltageSevereOvervoltageProtection(45), // + Phase1GridVoltageGeneralOvervoltageProtection(46), // + Phase2GridVoltageSevereOvervoltageProtection(47), // + Phase2GridVoltageGeneralOvervoltageProtection(48), // + Phase3GridVoltageSevereOvervoltageProtection(49), // + Phase3GridVoltageGeneralOvervoltageProtection(50), // + Phase1GridVoltageSevereUndervoltageProtection(51), // + Phase1GridVoltageGeneralUndervoltageProtection(52), // + Phase2GridVoltageSevereUndervoltageProtection(53), // + Phase2GridVoltageGeneralUndervoltageProtection(54), // + Phase3GridVoltageSevereUndervoltageProtection(55), // + Phase3GridVoltageGeneralUndervoltageProtection(56), // + SevereOverfrequncyProtection(57), // + GeneralOverfrequncyProtection(58), // + SevereUnderfrequncyProtection(59), // + GeneralsUnderfrequncyProtection(60), // + Phase1Gridloss(61), // + Phase2Gridloss(62), // + Phase3Gridloss(63), // + IslandingProtection(64), // + Phase1UnderVoltageRideThrough(65), // + Phase2UnderVoltageRideThrough(66), // + Phase3UnderVoltageRideThrough(67), // + Phase1InverterVoltageSevereOvervoltageProtection(68), // + Phase1InverterVoltageGeneralOvervoltageProtection(69), // + Phase2InverterVoltageSevereOvervoltageProtection(70), // + Phase2InverterVoltageGeneralOvervoltageProtection(71), // + Phase3InverterVoltageSevereOvervoltageProtection(72), // + Phase3InverterVoltageGeneralOvervoltageProtection(73), // + InverterPeakVoltageHighProtectionCauseByACDisconnect(74); + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/WarningCharger.java b/edge/src/io/openems/impl/device/commercial/WarningCharger.java index 3f5cb977a56..773b0b3fc82 100644 --- a/edge/src/io/openems/impl/device/commercial/WarningCharger.java +++ b/edge/src/io/openems/impl/device/commercial/WarningCharger.java @@ -1,57 +1,192 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningCharger implements WarningEnum{ - CurrentSamplingChannelAbnormityOnHighVoltageSide(0), CurrentSamplingChannelAbnormityOnLowVoltageSide(1), BmsDCDC1EEPROMParametersOverRange(2),EEPROMParametersOverRange(3), UpdateEEPROMFailed(4), - ReadEEPROMFailed(5), CurrentSamplingChannelAbnormityBeforeInductance(6), ReactorPowerDecreaseCausedByOvertemperature(7), IGBTPowerDecreaseCausedByOvertemperature(8), - TemperatureChanel3PowerDecreaseCausedByOvertemperature(9), TemperatureChanel4PowerDecreaseCausedByOvertemperature(10), TemperatureChanel5PowerDecreaseCausedByOvertemperature(11), - TemperatureChanel6PowerDecreaseCausedByOvertemperature(12), TemperatureChanel7PowerDecreaseCausedByOvertemperature(13), TemperatureChanel8PowerDecreaseCausedByOvertemperature(14), - Fan1StopFailed(15), Fan2StopFailed(16), Fan3StopFailed(17), Fan4StopFailed(18), Fan1StartupFailed(19), Fan2StartupFailed(20), Fan3StartupFailed(21), Fan4StartupFailed(22), - HighVoltageSideOvervoltage(23), HighVoltageSideUndervoltage(24), HighVoltageSideVoltageChangeUnconventionally(25),CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(26), - CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(27), InitialDutyRatioAbnormityBeforeDCConverterWork(28),VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(29), - VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(30), HighVoltageBreakerInspectionAbnormity(31),LowVoltageBreakerInspectionAbnormity(32), BsmDCDC5DCPrechargeContactorInspectionAbnormity(33), - DCPrechargeContactorOpenUnsuccessfully(34), DCMainContactorInspectionAbnormity(35),DCMainContactorOpenUnsuccessfully(36), OutputContactorCloseUnsuccessfully(37), OutputContactorOpenUnsuccessfully(38), - ACMainContactorCloseUnsuccessfully(39), ACMainContactorOpenUnsuccessfully(40),NegContactorOpenUnsuccessfully(41), NegContactorCloseUnsuccessfully(42), NegContactorStateAbnormal(43), - BsmDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(44), BsmDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(45), BsmDCDC1EEPROMParametersOverRange(46), BsmDCDC1UpdateEEPROMFailed(47), - BsmDCDC1ReadEEPROMFailed(48), BsmDCDC1CurrentSamplingChannelAbnormityBeforeInductance(49), BsmDCDC1ReactorPowerDecreaseCausedByOvertemperature(50), BsmDCDC1IGBTPowerDecreaseCausedByOvertemperature(51), - BsmDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(52), BsmDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(53), BsmDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(54), - BsmDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(55), BsmDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(56), BsmDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(57), - BsmDCDC1Fan1StopFailed(58), BsmDCDC1Fan2StopFailed(59), BsmDCDC1Fan3StopFailed(60), BsmDCDC1Fan4StopFailed(61), BsmDCDC1Fan1StartupFailed(62), BsmDCDC1Fan2StartupFailed(63), BsmDCDC1Fan3StartupFailed(64), - BsmDCDC1Fan4StartupFailed(65), BsmDCDC1HighVoltageSideOvervoltage(66), BsmDCDC1HighVoltageSideUndervoltage(67), BsmDCDC1HighVoltageSideVoltageChangeUnconventionally(68), BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(69), - BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(70), BmsDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(71), BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(72), - BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(73), BmsDCDC1HighVoltageBreakerInspectionAbnormity(74), BmsDCDC1LowVoltageBreakerInspectionAbnormity(75), BmsDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(76), - BmsDCDC1DCPrechargeContactorOpenUnsuccessfully(77), BmsDCDC1DCMainContactorInspectionAbnormity(78), BmsDCDC1DCMainContactorOpenUnsuccessfully(79), BmsDCDC1OutputContactorCloseUnsuccessfully(80), - BmsDCDC1OutputContactorOpenUnsuccessfully(81), BmsDCDC1ACMainContactorCloseUnsuccessfully(82), BmsDCDC1ACMainContactorOpenUnsuccessfully(83), BmsDCDC1NegContactorOpenUnsuccessfully(84), - BmsDCDC1NegContactorCloseUnsuccessfully(85), BmsDCDC1NegContactorStateAbnormal(86), PvDCDCCurrentSamplingChannelAbnormityOnHighVoltageSide(87), PvDCDCCurrentSamplingChannelAbnormityOnLowVoltageSide(88), - PvDCDCEEPROMParametersOverRange(89), PvDCDCUpdateEEPROMFailed(90), PvDCDCReadEEPROMFailed(91), PvDCDCCurrentSamplingChannelAbnormityBeforeInductance(92), PvDCDCReactorPowerDecreaseCausedByOvertemperature(93), - PvDCDCIGBTPowerDecreaseCausedByOvertemperature(94), PvDCDCTemperatureChanel3PowerDecreaseCausedByOvertemperature(95), PvDCDCTemperatureChanel4PowerDecreaseCausedByOvertemperature(96), PvDCDCTemperatureChanel5PowerDecreaseCausedByOvertemperature(97), - PvDCDCTemperatureChanel6PowerDecreaseCausedByOvertemperature(98), PvDCDCTemperatureChanel7PowerDecreaseCausedByOvertemperature(99), PvDCDCTemperatureChanel8PowerDecreaseCausedByOvertemperature(100), - PvDCDCFan1StopFailed(101), PvDCDCFan2StopFailed(102), PvDCDCFan3StopFailed(103), PvDCDCFan4StopFailed(104), PvDCDCFan1StartupFailed(105), PvDCDCFan2StartupFailed(106), PvDCDCFan3StartupFailed(107), - PvDCDCFan4StartupFailed(108), PvDCDCHighVoltageSideOvervoltage(109), PvDCDCHighVoltageSideUndervoltage(110), PvDCDCHighVoltageSideVoltageChangeUnconventionally(111), PvDCDCCurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(112), - PvDCDCCurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(113), PvDCDCInitialDutyRatioAbnormityBeforeDCConverterWork(114), PvDCDCVoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(115), - PvDCDCVoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(116), PvDCDCHighVoltageBreakerInspectionAbnormity(117), PvDCDCLowVoltageBreakerInspectionAbnormity(118), PvDCDCBsmDCDC5DCPrechargeContactorInspectionAbnormity(119), - PvDCDCDCPrechargeContactorOpenUnsuccessfully(120), PvDCDCDCMainContactorInspectionAbnormity(121), PvDCDCDCMainContactorOpenUnsuccessfully(122), PvDCDCOutputContactorCloseUnsuccessfully(123), PvDCDCOutputContactorOpenUnsuccessfully(124), - PvDCDCACMainContactorCloseUnsuccessfully(125), PvDCDCACMainContactorOpenUnsuccessfully(126), PvDCDCNegContactorOpenUnsuccessfully(127), PvDCDCNegContactorCloseUnsuccessfully(128), PvDCDCNegContactorStateAbnormal(129), - PvDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(130), PvDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(131), PvDCDC1EEPROMParametersOverRange(132), PvDCDC1UpdateEEPROMFailed(133), PvDCDC1ReadEEPROMFailed(134), - PvDCDC1CurrentSamplingChannelAbnormityBeforeInductance(135), PvDCDC1ReactorPowerDecreaseCausedByOvertemperature(136), PvDCDC1IGBTPowerDecreaseCausedByOvertemperature(137), PvDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(138), - PvDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(139), PvDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(140), PvDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(141), PvDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(142), - PvDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(143), PvDCDC1Fan1StopFailed(144), PvDCDC1Fan2StopFailed(145), PvDCDC1Fan3StopFailed(146), PvDCDC1Fan4StopFailed(147), PvDCDC1Fan1StartupFailed(148), PvDCDC1Fan2StartupFailed(149), - PvDCDC1Fan3StartupFailed(150), PvDCDC1Fan4StartupFailed(151), PvDCDC1HighVoltageSideOvervoltage(152), PvDCDC1HighVoltageSideUndervoltage(153), PvDCDC1HighVoltageSideVoltageChangeUnconventionally(154), PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(155), - PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(156), PvDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(157), PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(158), PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(159), - PvDCDC1HighVoltageBreakerInspectionAbnormity(160), PvDCDC1LowVoltageBreakerInspectionAbnormity(161), PvDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(162), PvDCDC1DCPrechargeContactorOpenUnsuccessfully(163), PvDCDC1DCMainContactorInspectionAbnormity(164), - PvDCDC1DCMainContactorOpenUnsuccessfully(165), PvDCDC1OutputContactorCloseUnsuccessfully(166), PvDCDC1OutputContactorOpenUnsuccessfully(167), PvDCDC1ACMainContactorCloseUnsuccessfully(168), PvDCDC1ACMainContactorOpenUnsuccessfully(169), - PvDCDC1NegContactorOpenUnsuccessfully(170), PvDCDC1NegContactorCloseUnsuccessfully(171), PvDCDC1NegContactorStateAbnormal(172); - - - public final int value; - - private WarningCharger(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconCommercialCharger.class) +public enum WarningCharger implements WarningEnum { + CurrentSamplingChannelAbnormityOnHighVoltageSide(0), // + CurrentSamplingChannelAbnormityOnLowVoltageSide(1), // + BmsDCDC1EEPROMParametersOverRange(2), // + EEPROMParametersOverRange(3), // + UpdateEEPROMFailed(4), // + ReadEEPROMFailed(5), // + CurrentSamplingChannelAbnormityBeforeInductance(6), // + ReactorPowerDecreaseCausedByOvertemperature(7), // + IGBTPowerDecreaseCausedByOvertemperature(8), // + TemperatureChanel3PowerDecreaseCausedByOvertemperature(9), // + TemperatureChanel4PowerDecreaseCausedByOvertemperature(10), // + TemperatureChanel5PowerDecreaseCausedByOvertemperature(11), // + TemperatureChanel6PowerDecreaseCausedByOvertemperature(12), // + TemperatureChanel7PowerDecreaseCausedByOvertemperature(13), // + TemperatureChanel8PowerDecreaseCausedByOvertemperature(14), // + Fan1StopFailed(15), // + Fan2StopFailed(16), // + Fan3StopFailed(17), // + Fan4StopFailed(18), // + Fan1StartupFailed(19), // + Fan2StartupFailed(20), // + Fan3StartupFailed(21), // + Fan4StartupFailed(22), // + HighVoltageSideOvervoltage(23), // + HighVoltageSideUndervoltage(24), // + HighVoltageSideVoltageChangeUnconventionally(25), // + CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(26), // + CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(27), // + InitialDutyRatioAbnormityBeforeDCConverterWork(28), // + VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(29), // + VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(30), // + HighVoltageBreakerInspectionAbnormity(31), // + LowVoltageBreakerInspectionAbnormity(32), // + BsmDCDC5DCPrechargeContactorInspectionAbnormity(33), // + DCPrechargeContactorOpenUnsuccessfully(34), // + DCMainContactorInspectionAbnormity(35), // + DCMainContactorOpenUnsuccessfully(36), // + OutputContactorCloseUnsuccessfully(37), // + OutputContactorOpenUnsuccessfully(38), // + ACMainContactorCloseUnsuccessfully(39), // + ACMainContactorOpenUnsuccessfully(40), // + NegContactorOpenUnsuccessfully(41), // + NegContactorCloseUnsuccessfully(42), // + NegContactorStateAbnormal(43), // + BsmDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(44), // + BsmDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(45), // + BsmDCDC1EEPROMParametersOverRange(46), // + BsmDCDC1UpdateEEPROMFailed(47), // + BsmDCDC1ReadEEPROMFailed(48), // + BsmDCDC1CurrentSamplingChannelAbnormityBeforeInductance(49), // + BsmDCDC1ReactorPowerDecreaseCausedByOvertemperature(50), // + BsmDCDC1IGBTPowerDecreaseCausedByOvertemperature(51), // + BsmDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(52), // + BsmDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(53), // + BsmDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(54), // + BsmDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(55), // + BsmDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(56), // + BsmDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(57), // + BsmDCDC1Fan1StopFailed(58), // + BsmDCDC1Fan2StopFailed(59), // + BsmDCDC1Fan3StopFailed(60), // + BsmDCDC1Fan4StopFailed(61), // + BsmDCDC1Fan1StartupFailed(62), // + BsmDCDC1Fan2StartupFailed(63), // + BsmDCDC1Fan3StartupFailed(64), // + BsmDCDC1Fan4StartupFailed(65), // + BsmDCDC1HighVoltageSideOvervoltage(66), // + BsmDCDC1HighVoltageSideUndervoltage(67), // + BsmDCDC1HighVoltageSideVoltageChangeUnconventionally(68), // + BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(69), // + BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(70), // + BmsDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(71), // + BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(72), // + BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(73), // + BmsDCDC1HighVoltageBreakerInspectionAbnormity(74), // + BmsDCDC1LowVoltageBreakerInspectionAbnormity(75), // + BmsDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(76), // + BmsDCDC1DCPrechargeContactorOpenUnsuccessfully(77), // + BmsDCDC1DCMainContactorInspectionAbnormity(78), // + BmsDCDC1DCMainContactorOpenUnsuccessfully(79), // + BmsDCDC1OutputContactorCloseUnsuccessfully(80), // + BmsDCDC1OutputContactorOpenUnsuccessfully(81), // + BmsDCDC1ACMainContactorCloseUnsuccessfully(82), // + BmsDCDC1ACMainContactorOpenUnsuccessfully(83), // + BmsDCDC1NegContactorOpenUnsuccessfully(84), // + BmsDCDC1NegContactorCloseUnsuccessfully(85), // + BmsDCDC1NegContactorStateAbnormal(86), // + PvDCDCCurrentSamplingChannelAbnormityOnHighVoltageSide(87), // + PvDCDCCurrentSamplingChannelAbnormityOnLowVoltageSide(88), // + PvDCDCEEPROMParametersOverRange(89), // + PvDCDCUpdateEEPROMFailed(90), // + PvDCDCReadEEPROMFailed(91), // + PvDCDCCurrentSamplingChannelAbnormityBeforeInductance(92), // + PvDCDCReactorPowerDecreaseCausedByOvertemperature(93), // + PvDCDCIGBTPowerDecreaseCausedByOvertemperature(94), // + PvDCDCTemperatureChanel3PowerDecreaseCausedByOvertemperature(95), // + PvDCDCTemperatureChanel4PowerDecreaseCausedByOvertemperature(96), // + PvDCDCTemperatureChanel5PowerDecreaseCausedByOvertemperature(97), // + PvDCDCTemperatureChanel6PowerDecreaseCausedByOvertemperature(98), // + PvDCDCTemperatureChanel7PowerDecreaseCausedByOvertemperature(99), // + PvDCDCTemperatureChanel8PowerDecreaseCausedByOvertemperature(100), // + PvDCDCFan1StopFailed(101), // + PvDCDCFan2StopFailed(102), // + PvDCDCFan3StopFailed(103), // + PvDCDCFan4StopFailed(104), // + PvDCDCFan1StartupFailed(105), // + PvDCDCFan2StartupFailed(106), // + PvDCDCFan3StartupFailed(107), // + PvDCDCFan4StartupFailed(108), // + PvDCDCHighVoltageSideOvervoltage(109), // + PvDCDCHighVoltageSideUndervoltage(110), // + PvDCDCHighVoltageSideVoltageChangeUnconventionally(111), // + PvDCDCCurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(112), // + PvDCDCCurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(113), // + PvDCDCInitialDutyRatioAbnormityBeforeDCConverterWork(114), // + PvDCDCVoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(115), // + PvDCDCVoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(116), // + PvDCDCHighVoltageBreakerInspectionAbnormity(117), // + PvDCDCLowVoltageBreakerInspectionAbnormity(118), // + PvDCDCBsmDCDC5DCPrechargeContactorInspectionAbnormity(119), // + PvDCDCDCPrechargeContactorOpenUnsuccessfully(120), // + PvDCDCDCMainContactorInspectionAbnormity(121), // + PvDCDCDCMainContactorOpenUnsuccessfully(122), // + PvDCDCOutputContactorCloseUnsuccessfully(123), // + PvDCDCOutputContactorOpenUnsuccessfully(124), // + PvDCDCACMainContactorCloseUnsuccessfully(125), // + PvDCDCACMainContactorOpenUnsuccessfully(126), // + PvDCDCNegContactorOpenUnsuccessfully(127), // + PvDCDCNegContactorCloseUnsuccessfully(128), // + PvDCDCNegContactorStateAbnormal(129), // + PvDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(130), // + PvDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(131), // + PvDCDC1EEPROMParametersOverRange(132), // + PvDCDC1UpdateEEPROMFailed(133), // + PvDCDC1ReadEEPROMFailed(134), // + PvDCDC1CurrentSamplingChannelAbnormityBeforeInductance(135), // + PvDCDC1ReactorPowerDecreaseCausedByOvertemperature(136), // + PvDCDC1IGBTPowerDecreaseCausedByOvertemperature(137), // + PvDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(138), // + PvDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(139), // + PvDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(140), // + PvDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(141), // + PvDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(142), // + PvDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(143), // + PvDCDC1Fan1StopFailed(144), // + PvDCDC1Fan2StopFailed(145), // + PvDCDC1Fan3StopFailed(146), // + PvDCDC1Fan4StopFailed(147), // + PvDCDC1Fan1StartupFailed(148), // + PvDCDC1Fan2StartupFailed(149), // + PvDCDC1Fan3StartupFailed(150), // + PvDCDC1Fan4StartupFailed(151), // + PvDCDC1HighVoltageSideOvervoltage(152), // + PvDCDC1HighVoltageSideUndervoltage(153), // + PvDCDC1HighVoltageSideVoltageChangeUnconventionally(154), // + PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(155), // + PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(156), // + PvDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(157), // + PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(158), // + PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(159), // + PvDCDC1HighVoltageBreakerInspectionAbnormity(160), // + PvDCDC1LowVoltageBreakerInspectionAbnormity(161), // + PvDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(162), // + PvDCDC1DCPrechargeContactorOpenUnsuccessfully(163), // + PvDCDC1DCMainContactorInspectionAbnormity(164), // + PvDCDC1DCMainContactorOpenUnsuccessfully(165), // + PvDCDC1OutputContactorCloseUnsuccessfully(166), // + PvDCDC1OutputContactorOpenUnsuccessfully(167), // + PvDCDC1ACMainContactorCloseUnsuccessfully(168), // + PvDCDC1ACMainContactorOpenUnsuccessfully(169), // + PvDCDC1NegContactorOpenUnsuccessfully(170), // + PvDCDC1NegContactorCloseUnsuccessfully(171), // + PvDCDC1NegContactorStateAbnormal(172); + + public final int value; + + private WarningCharger(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/WarningEss.java b/edge/src/io/openems/impl/device/commercial/WarningEss.java index 9ea9d403b9b..dedde590d4c 100644 --- a/edge/src/io/openems/impl/device/commercial/WarningEss.java +++ b/edge/src/io/openems/impl/device/commercial/WarningEss.java @@ -1,66 +1,69 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum { - EmergencyStop(0), // - KeyManualStop(1), // - TransformerPhaseBTemperatureSensorInvalidation(2), // - SDMemoryCardInvalidation(4), // - InverterCommunicationAbnormity(5), // - BatteryStackCommunicationAbnormity(6), // - MultifunctionalAmmeterCommunicationAbnormity(7), // - RemoteCommunicationAbnormity(8), // - PVDC1CommunicationAbnormity(9), // - PVDC2CommunicationAbnormity(10), // - TransformerSevereOvertemperature(11), // - DCPrechargeContactorInspectionAbnormity(12), // - DCBreaker1InspectionAbnormity(13), // - DCBreaker2InspectionAbnormity(14), // - ACPrechargeContactorInspectionAbnormity(15), // - ACMainontactorInspectionAbnormity(16), // - ACBreakerInspectionAbnormity(17), // - DCBreaker1CloseUnsuccessfully(18), // - DCBreaker2CloseUnsuccessfully(19), // - ControlSignalCloseAbnormallyInspectedBySystem(20), // - ControlSignalOpenAbnormallyInspectedBySystem(21), // - NeutralWireContactorCloseUnsuccessfully(22), // - NeutralWireContactorOpenUnsuccessfully(23), // - WorkDoorOpen(24), // - Emergency1Stop(25), // - ACBreakerCloseUnsuccessfully(26), // - ControlSwitchStop(27), // - GeneralOverload(28), SevereOverload(29), // - BatteryCurrentOverLimit(30), // - PowerDecreaseCausedByOvertemperature(31), // - InverterGeneralOvertemperature(32), // - ACThreePhaseCurrentUnbalance(33), // - RestoreFactorySettingUnsuccessfully(34), // - PoleBoardInvalidation(35), // - SelfInspectionFailed(36), // - ReceiveBMSFaultAndStop(37), // - RefrigerationEquipmentinvalidation(38), // - LargeTemperatureDifferenceAmongIGBTThreePhases(39), // - EEPROMParametersOverRange(40), // - EEPROMParametersBackupFailed(41), // - DCBreakerCloseunsuccessfully(42), // - CommunicationBetweenInverterAndBSMUDisconnected(43), // - CommunicationBetweenInverterAndMasterDisconnected(44), // - CommunicationBetweenInverterAndUCDisconnected(45), // - BMSStartOvertimeControlledByPCS(46), // - BMSStopOvertimeControlledByPCS(47), // - SyncSignalInvalidation(48), // - SyncSignalContinuousCaputureFault(49), // - SyncSignalSeveralTimesCaputureFault(50); - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconCommercialEss.class) +public enum WarningEss implements WarningEnum { + + EmergencyStop(0), // + KeyManualStop(1), // + TransformerPhaseBTemperatureSensorInvalidation(2), // + SDMemoryCardInvalidation(4), // + InverterCommunicationAbnormity(5), // + BatteryStackCommunicationAbnormity(6), // + MultifunctionalAmmeterCommunicationAbnormity(7), // + RemoteCommunicationAbnormity(8), // + PVDC1CommunicationAbnormity(9), // + PVDC2CommunicationAbnormity(10), // + TransformerSevereOvertemperature(11), // + DCPrechargeContactorInspectionAbnormity(12), // + DCBreaker1InspectionAbnormity(13), // + DCBreaker2InspectionAbnormity(14), // + ACPrechargeContactorInspectionAbnormity(15), // + ACMainontactorInspectionAbnormity(16), // + ACBreakerInspectionAbnormity(17), // + DCBreaker1CloseUnsuccessfully(18), // + DCBreaker2CloseUnsuccessfully(19), // + ControlSignalCloseAbnormallyInspectedBySystem(20), // + ControlSignalOpenAbnormallyInspectedBySystem(21), // + NeutralWireContactorCloseUnsuccessfully(22), // + NeutralWireContactorOpenUnsuccessfully(23), // + WorkDoorOpen(24), // + Emergency1Stop(25), // + ACBreakerCloseUnsuccessfully(26), // + ControlSwitchStop(27), // + GeneralOverload(28), SevereOverload(29), // + BatteryCurrentOverLimit(30), // + PowerDecreaseCausedByOvertemperature(31), // + InverterGeneralOvertemperature(32), // + ACThreePhaseCurrentUnbalance(33), // + RestoreFactorySettingUnsuccessfully(34), // + PoleBoardInvalidation(35), // + SelfInspectionFailed(36), // + ReceiveBMSFaultAndStop(37), // + RefrigerationEquipmentinvalidation(38), // + LargeTemperatureDifferenceAmongIGBTThreePhases(39), // + EEPROMParametersOverRange(40), // + EEPROMParametersBackupFailed(41), // + DCBreakerCloseunsuccessfully(42), // + CommunicationBetweenInverterAndBSMUDisconnected(43), // + CommunicationBetweenInverterAndMasterDisconnected(44), // + CommunicationBetweenInverterAndUCDisconnected(45), // + BMSStartOvertimeControlledByPCS(46), // + BMSStopOvertimeControlledByPCS(47), // + SyncSignalInvalidation(48), // + SyncSignalContinuousCaputureFault(49), // + SyncSignalSeveralTimesCaputureFault(50); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/mini/FaultEss.java b/edge/src/io/openems/impl/device/mini/FaultEss.java index 7cb96a7ec7c..3bff9392afa 100644 --- a/edge/src/io/openems/impl/device/mini/FaultEss.java +++ b/edge/src/io/openems/impl/device/mini/FaultEss.java @@ -1,28 +1,65 @@ -package io.openems.impl.device.mini; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum{ - ControlCurrentOverload100Percent(0), ControlCurrentOverload110Percent(1), ControlCurrentOverload150Percent(2), ControlCurrentOverload200Percent(3), - ControlCurrentOverload120Percent(4), ControlCurrentOverload300Percent(5), ControlTransientLoad300Percent(6), GridOverCurrent(7), LockingWaveformTooManyTimes(8), - InverterVoltageZeroDriftError(9), GridVoltageZeroDriftError(10), ControlCurrentZeroDriftError(11), InverterCurrentZeroDriftError(12), GridCurrentZeroDriftError(13), - PDPProtection(14), HardwareControlCurrentProtection(15), HardwareACVoltProtection(16), HardwareDCCurrentProtection(17), HardwareTemperatureProtection(18), - NoCapturingSignal(19), DCOvervoltage(20), DCDisconnected(21), InverterUndervoltage(22), InverterOvervoltage(23), CurrentSensorFail(24), VoltageSensorFail(25), - PowerUncontrollable(26), CurrentUncontrollable(27), FanError(28), PhaseLack(29), InverterRelayFault(30), GridRelayFault(31), ControlPanelOvertemp(32), PowerPanelOvertemp(33), - DCInputOvercurrent(34), CapacitorOvertemp(35), RadiatorOvertemp(36), TransformerOvertemp(37), CombinationCommError(38), EEPROMError(39), LoadCurrentZeroDriftError(40), - CurrentLimitRError(41), PhaseSyncError(42), ExternalPVCurrentZeroDriftError(43), ExternalGridCurrentZeroDriftError(44); - - - - - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.mini; + +import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconMiniEss.class) +public enum FaultEss implements FaultEnum { + + ControlCurrentOverload100Percent(0), // + ControlCurrentOverload110Percent(1), // + ControlCurrentOverload150Percent(2), // + ControlCurrentOverload200Percent(3), // + ControlCurrentOverload120Percent(4), // + ControlCurrentOverload300Percent(5), // + ControlTransientLoad300Percent(6), // + GridOverCurrent(7), // + LockingWaveformTooManyTimes(8), // + InverterVoltageZeroDriftError(9), // + GridVoltageZeroDriftError(10), // + ControlCurrentZeroDriftError(11), // + InverterCurrentZeroDriftError(12), // + GridCurrentZeroDriftError(13), // + PDPProtection(14), // + HardwareControlCurrentProtection(15), // + HardwareACVoltProtection(16), // + HardwareDCCurrentProtection(17), // + HardwareTemperatureProtection(18), // + NoCapturingSignal(19), // + DCOvervoltage(20), // + DCDisconnected(21), // + InverterUndervoltage(22), // + InverterOvervoltage(23), // + CurrentSensorFail(24), // + VoltageSensorFail(25), // + PowerUncontrollable(26), // + CurrentUncontrollable(27), // + FanError(28), // + PhaseLack(29), // + InverterRelayFault(30), // + GridRelayFault(31), // + ControlPanelOvertemp(32), // + PowerPanelOvertemp(33), // + DCInputOvercurrent(34), // + CapacitorOvertemp(35), // + RadiatorOvertemp(36), // + TransformerOvertemp(37), // + CombinationCommError(38), // + EEPROMError(39), // + LoadCurrentZeroDriftError(40), // + CurrentLimitRError(41), // + PhaseSyncError(42), // + ExternalPVCurrentZeroDriftError(43), // + ExternalGridCurrentZeroDriftError(44); + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/mini/WarningEss.java b/edge/src/io/openems/impl/device/mini/WarningEss.java index acd33576da4..69cdeab6274 100644 --- a/edge/src/io/openems/impl/device/mini/WarningEss.java +++ b/edge/src/io/openems/impl/device/mini/WarningEss.java @@ -1,21 +1,38 @@ -package io.openems.impl.device.mini; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum{ - FailTheSystemShouldBeStopped(0), CommonLowVoltageAlarm(1), CommonHighVoltageAlarm(2), ChargingOverCurrentAlarm(3), - DischargingOverCurrentAlarm(4), OverTemperatureAlarm(5), InteralCommunicationAbnormal(6), GridUndervoltage(7), - GridOvervoltage(8), GridUnderFrequency(9), GridOverFrequency(10), GridPowerSupplyOff(11), GridConditionUnmeet(12), - DCUnderVoltage(13), InputOverResistance(14), CombinationError(15), CommWithInverterError(16), TmeError(17); - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.mini; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconMiniEss.class) +public enum WarningEss implements WarningEnum { + + FailTheSystemShouldBeStopped(0), // + CommonLowVoltageAlarm(1), // + CommonHighVoltageAlarm(2), // + ChargingOverCurrentAlarm(3), // + DischargingOverCurrentAlarm(4), // + OverTemperatureAlarm(5), // + InteralCommunicationAbnormal(6), // + GridUndervoltage(7), // + GridOvervoltage(8), // + GridUnderFrequency(9), // + GridOverFrequency(10), // + GridPowerSupplyOff(11), // + GridConditionUnmeet(12), // + DCUnderVoltage(13), // + InputOverResistance(14), // + CombinationError(15), // + CommWithInverterError(16), // + TmeError(17); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/minireadonly/FaultEss.java b/edge/src/io/openems/impl/device/minireadonly/FaultEss.java index 877cd7ad862..3c734343b68 100644 --- a/edge/src/io/openems/impl/device/minireadonly/FaultEss.java +++ b/edge/src/io/openems/impl/device/minireadonly/FaultEss.java @@ -1,27 +1,70 @@ -package io.openems.impl.device.minireadonly; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum -{ - - BECU1DischargeSevereOvercurrent(0), BECU1ChargeSevereOvercurrent(1), BECU1GeneralUndervoltage(2), BECU1SevereOvervoltage(3), BECU1GeneralOvervoltage(4), BECU1SevereUndervoltage(5), BECU1InsideCANBroken(6), - BECU1GeneralUndervoltageHighCurrentDischarge(7), BECU1BMUError(8), BECU1CurrentSamplingInvalidation(9), BECU1BatteryFail(10), BECU1TemperatureSamplingBroken(11), BECU1Contactor1TestBackIsAbnormalTurnOnAbnormity(12), - BECU1Contactor1TestBackIsAbnormalTurnOffAbnormity(13), BECU1Contactor2TestBackIsAbnormalTurnOnAbnormity(14), BECU1Contactor2TestBackIsAbnormalTurnOffAbnormity(15), BECU1SevereHighTemperatureFault(16), - BECU1HallInvalidation(17), BECU1ContactorInvalidation(18), BECU1OutsideCANBroken(19), BECU1CathodeContactorBroken(20), BECU2DischargeSevereOvercurrent(21), BECU2ChargeSevereOvercurrent(22), BECU2GeneralUndervoltage(23), - BECU2SevereOvervoltage(24), BECU2GeneralOvervoltage(25), BECU2SevereUndervoltage(26), BECU2InsideCANBroken(27), BECU2GeneralUndervoltageHighCurrentDischarge(28), BECU2BMUError(29), BECU2CurrentSamplingInvalidation(30), - BECU2BatteryFail(31), BECU2TemperatureSamplingBroken(32), BECU2Contactor1TestBackIsAbnormalTurnOnAbnormity(33), BECU2Contactor1TestBackIsAbnormalTurnOffAbnormity(34), BECU2Contactor2TestBackIsAbnormalTurnOnAbnormity(35), - BECU2Contactor2TestBackIsAbnormalTurnOffAbnormity(36), BECU2SevereHighTemperatureFault(37), BECU2HallInvalidation(38), BECU2ContactorInvalidation(39), BECU2OutsideCANBroken(40), BECU2CathodeContactorBroken(41), - NoAvailableBatteryGroup(42), StackGeneralLeakage(43), StackSevereLeakage(44), StackStartingFail(45), StackStoppingFail(46), BatteryProtection(47), StackAndGroup1CANCommunicationInterrupt(48), - StackAndGroup2CANCommunicationInterrupt(49); - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.minireadonly; + +import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconMiniEss.class) +public enum FaultEss implements FaultEnum { + + BECU1DischargeSevereOvercurrent(0), // + BECU1ChargeSevereOvercurrent(1), // + BECU1GeneralUndervoltage(2), // + BECU1SevereOvervoltage(3), // + BECU1GeneralOvervoltage(4), // + BECU1SevereUndervoltage(5), // + BECU1InsideCANBroken(6), // + BECU1GeneralUndervoltageHighCurrentDischarge(7), // + BECU1BMUError(8), // + BECU1CurrentSamplingInvalidation(9), // + BECU1BatteryFail(10), // + BECU1TemperatureSamplingBroken(11), // + BECU1Contactor1TestBackIsAbnormalTurnOnAbnormity(12), // + BECU1Contactor1TestBackIsAbnormalTurnOffAbnormity(13), // + BECU1Contactor2TestBackIsAbnormalTurnOnAbnormity(14), // + BECU1Contactor2TestBackIsAbnormalTurnOffAbnormity(15), // + BECU1SevereHighTemperatureFault(16), // + BECU1HallInvalidation(17), // + BECU1ContactorInvalidation(18), // + BECU1OutsideCANBroken(19), // + BECU1CathodeContactorBroken(20), // + BECU2DischargeSevereOvercurrent(21), // + BECU2ChargeSevereOvercurrent(22), // + BECU2GeneralUndervoltage(23), // + BECU2SevereOvervoltage(24), // + BECU2GeneralOvervoltage(25), // + BECU2SevereUndervoltage(26), // + BECU2InsideCANBroken(27), // + BECU2GeneralUndervoltageHighCurrentDischarge(28), // + BECU2BMUError(29), // + BECU2CurrentSamplingInvalidation(30), // + BECU2BatteryFail(31), // + BECU2TemperatureSamplingBroken(32), // + BECU2Contactor1TestBackIsAbnormalTurnOnAbnormity(33), // + BECU2Contactor1TestBackIsAbnormalTurnOffAbnormity(34), // + BECU2Contactor2TestBackIsAbnormalTurnOnAbnormity(35), // + BECU2Contactor2TestBackIsAbnormalTurnOffAbnormity(36), // + BECU2SevereHighTemperatureFault(37), // + BECU2HallInvalidation(38), // + BECU2ContactorInvalidation(39), // + BECU2OutsideCANBroken(40), // + BECU2CathodeContactorBroken(41), // + NoAvailableBatteryGroup(42), // + StackGeneralLeakage(43), // + StackSevereLeakage(44), // + StackStartingFail(45), // + StackStoppingFail(46), // + BatteryProtection(47), // + StackAndGroup1CANCommunicationInterrupt(48), // + StackAndGroup2CANCommunicationInterrupt(49); + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/minireadonly/WarningEss.java b/edge/src/io/openems/impl/device/minireadonly/WarningEss.java index 7031834556d..9ac68e7c609 100644 --- a/edge/src/io/openems/impl/device/minireadonly/WarningEss.java +++ b/edge/src/io/openems/impl/device/minireadonly/WarningEss.java @@ -1,38 +1,113 @@ -package io.openems.impl.device.minireadonly; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum -{ - BECU1GeneralChargeOverCurrentAlarm(0), BECU1GeneralDischargeOverCurrentAlarm(1), BECU1ChargeCurrentLimitAlarm(2), BECU1DischargeCurrentLimitAlarm(3), - BECU1GeneralHighVoltageAlarm(4), BECU1GeneralLowVoltageAlarm(5), BECU1AbnormalVoltageChangeAlarm(6), BECU1GeneralHighTemperatureAlarm(7),BECU1GeneralLowTemperatureAlarm(8), - BECU1AbnormalTemperatureChangeAlarm(9), BECU1SevereHighVoltageAlarm(10), BECU1SevereLowVoltageAlarm(11),BECU1SevereLowTemperatureAlarm(12), BECU1SeverveChargeOverCurrentAlarm(13), - BECU1SeverveDischargeOverCurrentAlarm(14), BECU1AbnormalCellCapacityAlarm(15), BECU1BalancedSamplingAlarm(16), BECU1BalancedControlAlarm(17), BECU1HallSensorDoesNotWorkAccurately(18), - BECU1Generalleakage(19), BECU1Severeleakage(20), BECU1Contactor1TurnOnAbnormity(21), BECU1Contactor1TurnOffAbnormity(22), BECU1Contactor2TurnOnAbnormity(23), BECU1Contactor2TurnOffAbnormity(24), - BECU1Contactor4CheckAbnormity(25), BECU1ContactorCurrentUnsafe(26), BECU1Contactor5CheckAbnormity(27), BECU1HighVoltageOffset(28), BECU1LowVoltageOffset(29), BECU1HighTemperatureOffset(30), - BECU2GeneralChargeOverCurrentAlarm(31), BECU2GeneralDischargeOverCurrentAlarm(32), BECU2ChargeCurrentLimitAlarm(33), BECU2DischargeCurrentLimitAlarm(34), BECU2GeneralHighVoltageAlarm(35), - BECU2GeneralLowVoltageAlarm(36), BECU2AbnormalVoltageChangeAlarm(37), BECU2GeneralHighTemperatureAlarm(38), BECU2GeneralLowTemperatureAlarm(39), BECU2AbnormalTemperatureChangeAlarm(40), - BECU2SevereHighVoltageAlarm(41), BECU2SevereLowVoltageAlarm(42), BECU2SevereLowTemperatureAlarm(43), BECU2SeverveChargeOverCurrentAlarm(44), BECU2SeverveDischargeOverCurrentAlarm(45), - BECU2AbnormalCellCapacityAlarm(46), BECU2BalancedSamplingAlarm(47), BECU2BalancedControlAlarm(48), BECU2HallSensorDoesNotWorkAccurately(49), BECU2Generalleakage(50), BECU2Severeleakage(51), - BECU2Contactor1TurnOnAbnormity(52), BECU2Contactor1TurnOffAbnormity(53), BECU2Contactor2TurnOnAbnormity(54), BECU2Contactor2TurnOffAbnormity(55), BECU2Contactor4CheckAbnormity(56), - BECU2ContactorCurrentUnsafe(57), BECU2Contactor5CheckAbnormity(58), BECU2HighVoltageOffset(59), BECU2LowVoltageOffset(60), BECU2HighTemperatureOffset(61), GeneralOvercurrentAlarmAtCellStackCharge(62), - GeneralOvercurrentAlarmAtCellStackDischarge(63), CurrentLimitAlarmAtCellStackCharge(64), CurrentLimitAlarmAtCellStackDischarge(65), GeneralCellStackHighVoltageAlarm(66), GeneralCellStackLowVoltageAlarm(67), - AbnormalCellStackVoltageChangeAlarm(68), GeneralCellStackHighTemperatureAlarm(69), GeneralCellStackLowTemperatureAlarm(70), AbnormalCellStackTemperatureChangeAlarm(71), SevereCellStackHighVoltageAlarm(72), - SevereCellStackLowVoltageAlarm(73), SevereCellStackLowTemperatureAlarm(74), SeverveOverCurrentAlarmAtCellStackDharge(75), SeverveOverCurrentAlarmAtCellStackDischarge(76), AbnormalCellStackCapacityAlarm(77), - TheParameterOfEEPROMInCellStackLoseEffectiveness(78), IsolatingSwitchInConfluenceArkBreak(79), TheCommunicationBetweenCellStackAndTemperatureOfCollectorBreak(80), TheTemperatureOfCollectorFail(81), - HallSensorDoNotWorkAccurately(82), TheCommunicationOfPCSBreak(83), AdvancedChargingOrMainContactorCloseAbnormally(84), AbnormalSampledVoltage(85), AbnormalAdvancedContactorOrAbnormalRS485GalleryOfPCS(86), - AbnormalMainContactor(87), GeneralCellStackLeakage(88), SevereCellStackLeakage(89), SmokeAlarm(90), TheCommunicationWireToAmmeterBreak(91), TheCommunicationWireToDredBreak(92); - - - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.minireadonly; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = FeneconMiniEss.class) +public enum WarningEss implements WarningEnum { + + BECU1GeneralChargeOverCurrentAlarm(0), // + BECU1GeneralDischargeOverCurrentAlarm(1), // + BECU1ChargeCurrentLimitAlarm(2), // + BECU1DischargeCurrentLimitAlarm(3), // + BECU1GeneralHighVoltageAlarm(4), // + BECU1GeneralLowVoltageAlarm(5), // + BECU1AbnormalVoltageChangeAlarm(6), // + BECU1GeneralHighTemperatureAlarm(7), // + BECU1GeneralLowTemperatureAlarm(8), // + BECU1AbnormalTemperatureChangeAlarm(9), // + BECU1SevereHighVoltageAlarm(10), // + BECU1SevereLowVoltageAlarm(11), // + BECU1SevereLowTemperatureAlarm(12), // + BECU1SeverveChargeOverCurrentAlarm(13), // + BECU1SeverveDischargeOverCurrentAlarm(14), // + BECU1AbnormalCellCapacityAlarm(15), // + BECU1BalancedSamplingAlarm(16), // + BECU1BalancedControlAlarm(17), // + BECU1HallSensorDoesNotWorkAccurately(18), // + BECU1Generalleakage(19), // + BECU1Severeleakage(20), // + BECU1Contactor1TurnOnAbnormity(21), // + BECU1Contactor1TurnOffAbnormity(22), // + BECU1Contactor2TurnOnAbnormity(23), // + BECU1Contactor2TurnOffAbnormity(24), // + BECU1Contactor4CheckAbnormity(25), // + BECU1ContactorCurrentUnsafe(26), // + BECU1Contactor5CheckAbnormity(27), // + BECU1HighVoltageOffset(28), // + BECU1LowVoltageOffset(29), // + BECU1HighTemperatureOffset(30), // + BECU2GeneralChargeOverCurrentAlarm(31), // + BECU2GeneralDischargeOverCurrentAlarm(32), // + BECU2ChargeCurrentLimitAlarm(33), // + BECU2DischargeCurrentLimitAlarm(34), // + BECU2GeneralHighVoltageAlarm(35), // + BECU2GeneralLowVoltageAlarm(36), // + BECU2AbnormalVoltageChangeAlarm(37), // + BECU2GeneralHighTemperatureAlarm(38), // + BECU2GeneralLowTemperatureAlarm(39), // + BECU2AbnormalTemperatureChangeAlarm(40), // + BECU2SevereHighVoltageAlarm(41), // + BECU2SevereLowVoltageAlarm(42), // + BECU2SevereLowTemperatureAlarm(43), // + BECU2SeverveChargeOverCurrentAlarm(44), // + BECU2SeverveDischargeOverCurrentAlarm(45), // + BECU2AbnormalCellCapacityAlarm(46), // + BECU2BalancedSamplingAlarm(47), // + BECU2BalancedControlAlarm(48), // + BECU2HallSensorDoesNotWorkAccurately(49), // + BECU2Generalleakage(50), // + BECU2Severeleakage(51), // + BECU2Contactor1TurnOnAbnormity(52), // + BECU2Contactor1TurnOffAbnormity(53), // + BECU2Contactor2TurnOnAbnormity(54), // + BECU2Contactor2TurnOffAbnormity(55), // + BECU2Contactor4CheckAbnormity(56), // + BECU2ContactorCurrentUnsafe(57), // + BECU2Contactor5CheckAbnormity(58), // + BECU2HighVoltageOffset(59), // + BECU2LowVoltageOffset(60), // + BECU2HighTemperatureOffset(61), // + GeneralOvercurrentAlarmAtCellStackCharge(62), // + GeneralOvercurrentAlarmAtCellStackDischarge(63), // + CurrentLimitAlarmAtCellStackCharge(64), // + CurrentLimitAlarmAtCellStackDischarge(65), // + GeneralCellStackHighVoltageAlarm(66), // + GeneralCellStackLowVoltageAlarm(67), // + AbnormalCellStackVoltageChangeAlarm(68), // + GeneralCellStackHighTemperatureAlarm(69), // + GeneralCellStackLowTemperatureAlarm(70), // + AbnormalCellStackTemperatureChangeAlarm(71), // + SevereCellStackHighVoltageAlarm(72), // + SevereCellStackLowVoltageAlarm(73), // + SevereCellStackLowTemperatureAlarm(74), // + SeverveOverCurrentAlarmAtCellStackDharge(75), // + SeverveOverCurrentAlarmAtCellStackDischarge(76), // + AbnormalCellStackCapacityAlarm(77), // + TheParameterOfEEPROMInCellStackLoseEffectiveness(78), // + IsolatingSwitchInConfluenceArkBreak(79), // + TheCommunicationBetweenCellStackAndTemperatureOfCollectorBreak(80), // + TheTemperatureOfCollectorFail(81), // + HallSensorDoNotWorkAccurately(82), // + TheCommunicationOfPCSBreak(83), // + AdvancedChargingOrMainContactorCloseAbnormally(84), // + AbnormalSampledVoltage(85), // + AbnormalAdvancedContactorOrAbnormalRS485GalleryOfPCS(86), // + AbnormalMainContactor(87), // + GeneralCellStackLeakage(88), // + SevereCellStackLeakage(89), // + SmokeAlarm(90), // + TheCommunicationWireToAmmeterBreak(91), // + TheCommunicationWireToDredBreak(92); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/pro/FaultEss.java b/edge/src/io/openems/impl/device/pro/FaultEss.java index c3e6be928f0..29354b5acf2 100644 --- a/edge/src/io/openems/impl/device/pro/FaultEss.java +++ b/edge/src/io/openems/impl/device/pro/FaultEss.java @@ -1,7 +1,9 @@ package io.openems.impl.device.pro; import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; +@ThingStateInfo(reference = FeneconProEss.class) public enum FaultEss implements FaultEnum { ControlCurrentOverload100PercentL1(0), // ControlCurrentOverload110PercentL1(1), // diff --git a/edge/src/io/openems/impl/device/pro/WarningEss.java b/edge/src/io/openems/impl/device/pro/WarningEss.java index 2b05dc1604c..59169949795 100644 --- a/edge/src/io/openems/impl/device/pro/WarningEss.java +++ b/edge/src/io/openems/impl/device/pro/WarningEss.java @@ -1,7 +1,9 @@ package io.openems.impl.device.pro; import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; +@ThingStateInfo(reference = FeneconProEss.class) public enum WarningEss implements WarningEnum { FailTheSystemShouldBeStopped(0), // CommonLowVoltageAlarm(1), // diff --git a/edge/src/io/openems/impl/device/pro/WarningPvMeter.java b/edge/src/io/openems/impl/device/pro/WarningPvMeter.java index 95f3b225909..e2f3f745d4d 100644 --- a/edge/src/io/openems/impl/device/pro/WarningPvMeter.java +++ b/edge/src/io/openems/impl/device/pro/WarningPvMeter.java @@ -1,7 +1,9 @@ package io.openems.impl.device.pro; import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; +@ThingStateInfo(reference = FeneconProPvMeter.class) public enum WarningPvMeter implements WarningEnum { NegativePowerL1(0), // NegativePowerL2(1), // diff --git a/edge/src/io/openems/impl/device/refu/FaultEss.java b/edge/src/io/openems/impl/device/refu/FaultEss.java index 8628d7a0188..ecad3b0f333 100644 --- a/edge/src/io/openems/impl/device/refu/FaultEss.java +++ b/edge/src/io/openems/impl/device/refu/FaultEss.java @@ -1,34 +1,82 @@ -package io.openems.impl.device.refu; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum{ - BMSInError(0), BMSInErrorSecond(1), BMSUndervoltage(2), BMSOvercurrent(3), ErrorBMSLimitsNotInitialized(4), ConnectError(5), OvervoltageWarning(6), - UndervoltageWarning(7), OvercurrentWarning(8), BMSReady(9), TREXReady(10), NoEnableBateryGroupOrUsableBatteryGroup(11), NormalLeakageOfBatteryGroup(12), - SeriousLeakageOfBatteryGroup(13), BatteryStartFailure(14), BatteryStopFailure(15), InterruptionOfCANCommunication(16),InterruptionOfCANCommunicationBetweenBatteryGroupAndController(17), - EmergencyStopAbnormalOfAuxiliaryCollector(18), LeakageSelfDetectionOnNegative(19), LeakageSelfDetectionOnPositive(20), SelfDetectionFailureOnBattery(21), - CANCommunicationInterruptionBetweenBatteryGroupAndGroup1(22),CANCommunicationInterruptionBetweenBatteryGroupAndGroup2(23), CANCommunicationInterruptionBetweenBatteryGroupAndGroup3(24), - CANCommunicationInterruptionBetweenBatteryGroupAndGroup4(25), MainContractorAbnormalInBatterySelfDetectGroup1(26), MainContractorAbnormalInBatterySelfDetectGroup2(27), - MainContractorAbnormalInBatterySelfDetectGroup3(28), MainContractorAbnormalInBatterySelfDetectGroup4(29), PreChargeContractorAbnormalOnBatterySelfDetectGroup1(30), - PreChargeContractorAbnormalOnBatterySelfDetectGroup2(31), PreChargeContractorAbnormalOnBatterySelfDetectGroup3(32), PreChargeContractorAbnormalOnBatterySelfDetectGroup4(33), - MainContactFailureOnBatteryControlGroup1(34), MainContactFailureOnBatteryControlGroup2(35), MainContactFailureOnBatteryControlGroup3(36), MainContactFailureOnBatteryControlGroup4(37), - PreChargeFailureOnBatteryControlGroup1(38), PreChargeFailureOnBatteryControlGroup2(39), PreChargeFailureOnBatteryControlGroup3(40), PreChargeFailureOnBatteryControlGroup4(41), - SamplingCircuitAbnormalForBMU(42), PowerCableDisconnectFailure(43), SamplingCircuitDisconnectFailure(44), CANDisconnectForMasterAndSlave(45), SammplingCircuitFailure(46), - SingleBatteryFailure(47), CircuitDetectionAbnormalForMainContactor(48), CircuitDetectionAbnormalForMainContactorSecond(49), CircuitDetectionAbnormalForFancontactor(50), - BMUPowerContactorCircuitDetectionAbnormal(51), CentralContactorCircuitDetectionAbnormal(52), SeriousTemperatureFault(53), CommunicationFaultForSystemController(54), - FrogAlarm(55), FuseFault(56), NormalLeakage(57), SeriousLeakage(58), CANDisconnectionBetweenBatteryGroupAndBatteryStack(59), CentralContactorCircuitOpen(60), - BMUPowerContactorOpen(61); - - - - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.refu; + +import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = RefuEss.class) +public enum FaultEss implements FaultEnum { + + BMSInError(0), // + BMSInErrorSecond(1), // + BMSUndervoltage(2), // + BMSOvercurrent(3), // + ErrorBMSLimitsNotInitialized(4), // + ConnectError(5), // + OvervoltageWarning(6), // + UndervoltageWarning(7), // + OvercurrentWarning(8), // + BMSReady(9), // + TREXReady(10), // + NoEnableBateryGroupOrUsableBatteryGroup(11), // + NormalLeakageOfBatteryGroup(12), // + SeriousLeakageOfBatteryGroup(13), // + BatteryStartFailure(14), // + BatteryStopFailure(15), // + InterruptionOfCANCommunication(16), // + InterruptionOfCANCommunicationBetweenBatteryGroupAndController(17), // + EmergencyStopAbnormalOfAuxiliaryCollector(18), // + LeakageSelfDetectionOnNegative(19), // + LeakageSelfDetectionOnPositive(20), // + SelfDetectionFailureOnBattery(21), // + CANCommunicationInterruptionBetweenBatteryGroupAndGroup1(22), // + CANCommunicationInterruptionBetweenBatteryGroupAndGroup2(23), // + CANCommunicationInterruptionBetweenBatteryGroupAndGroup3(24), // + CANCommunicationInterruptionBetweenBatteryGroupAndGroup4(25), // + MainContractorAbnormalInBatterySelfDetectGroup1(26), // + MainContractorAbnormalInBatterySelfDetectGroup2(27), // + MainContractorAbnormalInBatterySelfDetectGroup3(28), // + MainContractorAbnormalInBatterySelfDetectGroup4(29), // + PreChargeContractorAbnormalOnBatterySelfDetectGroup1(30), // + PreChargeContractorAbnormalOnBatterySelfDetectGroup2(31), // + PreChargeContractorAbnormalOnBatterySelfDetectGroup3(32), // + PreChargeContractorAbnormalOnBatterySelfDetectGroup4(33), // + MainContactFailureOnBatteryControlGroup1(34), // + MainContactFailureOnBatteryControlGroup2(35), // + MainContactFailureOnBatteryControlGroup3(36), // + MainContactFailureOnBatteryControlGroup4(37), // + PreChargeFailureOnBatteryControlGroup1(38), // + PreChargeFailureOnBatteryControlGroup2(39), // + PreChargeFailureOnBatteryControlGroup3(40), // + PreChargeFailureOnBatteryControlGroup4(41), // + SamplingCircuitAbnormalForBMU(42), // + PowerCableDisconnectFailure(43), // + SamplingCircuitDisconnectFailure(44), // + CANDisconnectForMasterAndSlave(45), // + SammplingCircuitFailure(46), // + SingleBatteryFailure(47), // + CircuitDetectionAbnormalForMainContactor(48), // + CircuitDetectionAbnormalForMainContactorSecond(49), // + CircuitDetectionAbnormalForFancontactor(50), // + BMUPowerContactorCircuitDetectionAbnormal(51), // + CentralContactorCircuitDetectionAbnormal(52), // + SeriousTemperatureFault(53), // + CommunicationFaultForSystemController(54), // + FrogAlarm(55), // + FuseFault(56), // + NormalLeakage(57), // + SeriousLeakage(58), // + CANDisconnectionBetweenBatteryGroupAndBatteryStack(59), // + CentralContactorCircuitOpen(60), // + BMUPowerContactorOpen(61); + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/refu/WarningEss.java b/edge/src/io/openems/impl/device/refu/WarningEss.java index 56f0bfef23b..3f54deaf9dc 100644 --- a/edge/src/io/openems/impl/device/refu/WarningEss.java +++ b/edge/src/io/openems/impl/device/refu/WarningEss.java @@ -1,29 +1,55 @@ -package io.openems.impl.device.refu; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum -{ - NormalChargingOverCurrent(0), CharginigCurrentOverLimit(1), DischargingCurrentOverLimit(2), NormalHighVoltage(3), NormalLowVoltage(4), - AbnormalVoltageVariation(5), NormalHighTemperature(6), NormalLowTemperature(7), AbnormalTemperatureVariation(8), SeriousHighVoltage(9), - SeriousLowVoltage(10), SeriousLowTemperature(11), ChargingSeriousOverCurrent(12), DischargingSeriousOverCurrent(13), AbnormalCapacityAlarm(14), - EEPROMParameterFailure(15), SwitchOfInsideCombinedCabinet(16), ShouldNotBeConnectedToGridDueToTheDCSideCondition(17), - EmergencyStopRequireFromSystemController(18), BatteryGroup1EnableAndNotConnectedToGrid(19), BatteryGroup2EnableAndNotConnectedToGrid(20), - BatteryGroup3EnableAndNotConnectedToGrid(21), BatteryGroup4EnableAndNotConnectedToGrid(22), TheIsolationSwitchOfBatteryGroup1Open(23), - TheIsolationSwitchOfBatteryGroup2Open(24), TheIsolationSwitchOfBatteryGroup3Open(25), TheIsolationSwitchOfBatteryGroup4Open(26), - BalancingSamplingFailureOfBatteryGroup1(27), BalancingSamplingFailureOfBatteryGroup2(28), BalancingSamplingFailureOfBatteryGroup3(29), - BalancingSamplingFailureOfBatteryGroup4(30), BalancingControlFailureOfBatteryGroup1(31), BalancingControlFailureOfBatteryGroup2(32), - BalancingControlFailureOfBatteryGroup3(33), BalancingControlFailureOfBatteryGroup4(34); - - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.refu; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = RefuEss.class) +public enum WarningEss implements WarningEnum { + + NormalChargingOverCurrent(0), // + CharginigCurrentOverLimit(1), // + DischargingCurrentOverLimit(2), // + NormalHighVoltage(3), // + NormalLowVoltage(4), // + AbnormalVoltageVariation(5), // + NormalHighTemperature(6), // + NormalLowTemperature(7), // + AbnormalTemperatureVariation(8), // + SeriousHighVoltage(9), // + SeriousLowVoltage(10), // + SeriousLowTemperature(11), // + ChargingSeriousOverCurrent(12), // + DischargingSeriousOverCurrent(13), // + AbnormalCapacityAlarm(14), // + EEPROMParameterFailure(15), // + SwitchOfInsideCombinedCabinet(16), // + ShouldNotBeConnectedToGridDueToTheDCSideCondition(17), // + EmergencyStopRequireFromSystemController(18), // + BatteryGroup1EnableAndNotConnectedToGrid(19), // + BatteryGroup2EnableAndNotConnectedToGrid(20), // + BatteryGroup3EnableAndNotConnectedToGrid(21), // + BatteryGroup4EnableAndNotConnectedToGrid(22), // + TheIsolationSwitchOfBatteryGroup1Open(23), // + TheIsolationSwitchOfBatteryGroup2Open(24), // + TheIsolationSwitchOfBatteryGroup3Open(25), // + TheIsolationSwitchOfBatteryGroup4Open(26), // + BalancingSamplingFailureOfBatteryGroup1(27), // + BalancingSamplingFailureOfBatteryGroup2(28), // + BalancingSamplingFailureOfBatteryGroup3(29), // + BalancingSamplingFailureOfBatteryGroup4(30), // + BalancingControlFailureOfBatteryGroup1(31), // + BalancingControlFailureOfBatteryGroup2(32), // + BalancingControlFailureOfBatteryGroup3(33), // + BalancingControlFailureOfBatteryGroup4(34); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/simulator/FaultEss.java b/edge/src/io/openems/impl/device/simulator/FaultEss.java index 7937bfa441d..f7af2e6f389 100644 --- a/edge/src/io/openems/impl/device/simulator/FaultEss.java +++ b/edge/src/io/openems/impl/device/simulator/FaultEss.java @@ -1,9 +1,11 @@ package io.openems.impl.device.simulator; import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; +@ThingStateInfo(reference = SimulatorAsymmetricEss.class) public enum FaultEss implements FaultEnum { - SimulatedError(0); + SimulatedFault(0); private final int value; diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java index 81298179375..e90aac87f12 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorAsymmetricEss.java @@ -62,6 +62,9 @@ public class SimulatorAsymmetricEss extends SimulatorDeviceNature private LoadGenerator offGridReactivePowerGenerator = new RandomLoadGenerator(); private ThingStateChannels thingState; + private final StaticThingStateChannel simulatedFault; + private final StaticThingStateChannel simulatedWarning; + /* * Constructors */ @@ -74,10 +77,8 @@ public SimulatorAsymmetricEss(String thingId, Device parent) throws ConfigExcept } }); this.thingState = new ThingStateChannels(this); - - StaticThingStateChannel tmp = new StaticThingStateChannel(FaultEss.SimulatedError, this, false); - tmp.setValue(true); - thingState.addFaultChannel(tmp); + thingState.addFaultChannel(this.simulatedFault = new StaticThingStateChannel(FaultEss.SimulatedFault, this, false)); + thingState.addWarningChannel(this.simulatedWarning = new StaticThingStateChannel(WarningEss.SimulatedWarning, this, false)); long initialSoc = SimulatorTools.addRandomLong(90, 90, 100, 5); this.energy = capacity.valueOptional().get() / 100 * initialSoc; @@ -293,6 +294,9 @@ protected void update() { } catch (InvalidValueException e) { e.printStackTrace(); } + // simulate faults and warnings + this.simulatedFault.setValue(SimulatorTools.getRandomBoolean()); + this.simulatedWarning.setValue(SimulatorTools.getRandomBoolean()); } @Override diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java index 6cff2e88e82..c2622895128 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorSymmetricEss.java @@ -35,7 +35,6 @@ import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.FunctionalReadChannel; import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.StaticThingStateChannel; import io.openems.api.channel.StaticValueChannel; import io.openems.api.channel.WriteChannel; import io.openems.api.channel.thingstate.ThingStateChannels; @@ -81,10 +80,6 @@ public SimulatorSymmetricEss(String thingId, Device parent) throws ConfigExcepti super(thingId, parent); this.thingState = new ThingStateChannels(this); - StaticThingStateChannel tmp = new StaticThingStateChannel(FaultEss.SimulatedError, this, false); - tmp.setValue(true); - thingState.addFaultChannel(tmp); - minSoc.addUpdateListener((channel, newValue) -> { // If chargeSoc was not set -> set it to minSoc minus 2 if (channel == minSoc && !chargeSoc.valueOptional().isPresent()) { diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorTools.java b/edge/src/io/openems/impl/device/simulator/SimulatorTools.java index b66fea0709a..5fc56c8dbb4 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorTools.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorTools.java @@ -1,37 +1,40 @@ -package io.openems.impl.device.simulator; - -import java.util.concurrent.ThreadLocalRandom; - -public class SimulatorTools { - - public static long addRandomLong(long value, long min, long max, int delta) { - long random = getRandomLong(delta * -1, delta); - value += random; - if (value > max) { - value = max; - } else if (value < min) { - value = min; - } - return value; - } - - public static long getRandomLong(int min, int max) { - return ThreadLocalRandom.current().nextLong(min, max + 1); - } - - public static double addRandomDouble(double value, double min, double max, double delta) { - double random = getRandomDouble(delta * -1, delta); - value += random; - if (value > max) { - value = max; - } else if (value < min) { - value = min; - } - return value; - } - - public static double getRandomDouble(double min, double max) { - return ThreadLocalRandom.current().nextDouble(min, max); - } - -} +package io.openems.impl.device.simulator; + +import java.util.concurrent.ThreadLocalRandom; + +public class SimulatorTools { + + public static long addRandomLong(long value, long min, long max, int delta) { + long random = getRandomLong(delta * -1, delta); + value += random; + if (value > max) { + value = max; + } else if (value < min) { + value = min; + } + return value; + } + + public static long getRandomLong(int min, int max) { + return ThreadLocalRandom.current().nextLong(min, max + 1); + } + + public static double addRandomDouble(double value, double min, double max, double delta) { + double random = getRandomDouble(delta * -1, delta); + value += random; + if (value > max) { + value = max; + } else if (value < min) { + value = min; + } + return value; + } + + public static double getRandomDouble(double min, double max) { + return ThreadLocalRandom.current().nextDouble(min, max); + } + + public static boolean getRandomBoolean() { + return ThreadLocalRandom.current().nextBoolean(); + } +} diff --git a/edge/src/io/openems/impl/protocol/modbus/FaultModbus.java b/edge/src/io/openems/impl/protocol/modbus/FaultModbus.java index 71624781ade..39995daebff 100644 --- a/edge/src/io/openems/impl/protocol/modbus/FaultModbus.java +++ b/edge/src/io/openems/impl/protocol/modbus/FaultModbus.java @@ -1,9 +1,11 @@ package io.openems.impl.protocol.modbus; import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.common.types.ThingStateInfo; +@ThingStateInfo(reference = { ModbusRtu.class, ModbusTcp.class }) public enum FaultModbus implements FaultEnum { - ConfigurationFault(0), + ConfigurationFault(0), // ConnectionFault(1); private final int value; diff --git a/io.openems.common/src/io/openems/common/types/ThingStateInfo.java b/io.openems.common/src/io/openems/common/types/ThingStateInfo.java new file mode 100644 index 00000000000..19fd6c198c0 --- /dev/null +++ b/io.openems.common/src/io/openems/common/types/ThingStateInfo.java @@ -0,0 +1,14 @@ +package io.openems.common.types; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({ TYPE }) +public @interface ThingStateInfo { + Class[] reference(); + /* TODO Change to Class after migration to OSGi */ +} From 7132f95c3735f9f7f3fb730556f1fa704093dc3b Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:06:57 +0100 Subject: [PATCH 129/156] Add Tools project for independent tools, that can be run individually --- tools/.classpath | 22 ++++++++++++ tools/.gitignore | 2 ++ tools/.project | 23 ++++++++++++ tools/.settings/org.eclipse.jdt.core.prefs | 12 +++++++ tools/.settings/org.eclipse.m2e.core.prefs | 4 +++ tools/pom.xml | 42 ++++++++++++++++++++++ 6 files changed, 105 insertions(+) create mode 100644 tools/.classpath create mode 100644 tools/.gitignore create mode 100644 tools/.project create mode 100644 tools/.settings/org.eclipse.jdt.core.prefs create mode 100644 tools/.settings/org.eclipse.m2e.core.prefs create mode 100644 tools/pom.xml diff --git a/tools/.classpath b/tools/.classpath new file mode 100644 index 00000000000..a97ee3659a8 --- /dev/null +++ b/tools/.classpath @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 00000000000..09e3bc9b241 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/tools/.project b/tools/.project new file mode 100644 index 00000000000..1046fea7030 --- /dev/null +++ b/tools/.project @@ -0,0 +1,23 @@ + + + tools + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + diff --git a/tools/.settings/org.eclipse.jdt.core.prefs b/tools/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..672496e107e --- /dev/null +++ b/tools/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/tools/.settings/org.eclipse.m2e.core.prefs b/tools/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000000..f897a7f1cb2 --- /dev/null +++ b/tools/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/tools/pom.xml b/tools/pom.xml new file mode 100644 index 00000000000..ecd4c677290 --- /dev/null +++ b/tools/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + tools + tools + 0.0.1-SNAPSHOT + + src + + + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + + + com.google.guava + guava + 23.1-jre + + + ch.qos.logback + logback-classic + 1.2.3 + + + org.slf4j + slf4j-api + 1.7.25 + + + com.google.code.gson + gson + 2.8.2 + + + \ No newline at end of file From 4c323efc0defa456e72ebb0237792b255f8f3b7d Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:07:22 +0100 Subject: [PATCH 130/156] Add ChannelExport tool by Matthias. Creates Readme.md files for documentation --- tools/src/tools/ChannelExport.java | 139 +++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tools/src/tools/ChannelExport.java diff --git a/tools/src/tools/ChannelExport.java b/tools/src/tools/ChannelExport.java new file mode 100644 index 00000000000..34631d9a457 --- /dev/null +++ b/tools/src/tools/ChannelExport.java @@ -0,0 +1,139 @@ +package tools; + +import java.io.FileWriter; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import io.openems.api.channel.Channel; +import io.openems.api.channel.ReadChannel; +import io.openems.api.device.Device; +import io.openems.api.doc.ChannelDoc; +import io.openems.api.doc.ThingDoc; +import io.openems.api.thing.Thing; +import io.openems.common.exceptions.OpenemsException; +import io.openems.core.ClassRepository; +import io.openems.impl.device.system.asymmetricsymmetriccombinationess.AsymmetricSymmetricCombinationEssNature; +import io.openems.impl.device.system.esscluster.EssClusterNature; +import io.openems.impl.protocol.modbus.ModbusDeviceNature; + +/** + * This generates 'Readme.md' files for OpenEMS Edge devices + * + */ +public class ChannelExport { + + public static void main(String[] args) throws OpenemsException { + String openemsPath = "C:\\Users\\matthias.rossmann\\Dev\\git\\openems-neu\\edge\\src"; + Collection deviceNatures; + HashMap files = new HashMap<>(); + try { + deviceNatures = ClassRepository.getInstance().getAvailableDeviceNatures(); + FileWriter devices = new FileWriter( + Paths.get(openemsPath, "\\io\\openems\\impl\\device\\Readme.md").toFile()); + devices.write("# List of implemented Devices.\r\n\r\n"); + for (ThingDoc thingDoc : deviceNatures) { + try { + System.out.println(thingDoc.getClazz().getName()); + if (thingDoc.getClazz().equals(AsymmetricSymmetricCombinationEssNature.class) + || thingDoc.getClazz().equals(EssClusterNature.class) || thingDoc.getClazz().isInterface() + || Modifier.isAbstract(thingDoc.getClazz().getModifiers())) { + continue; + } + Path p = Paths.get(openemsPath, + thingDoc.getClazz().getName().replaceAll("[^\\.]*$", "").replace(".", "/"), "Readme.md"); + FileWriter fw; + if (files.containsKey(p)) { + fw = files.get(p); + } else { + fw = new FileWriter(p.toFile()); + files.put(p, fw); + fw.write(""); + } + fw.append("# " + thingDoc.getTitle() + "\r\n" + thingDoc.getText() + + "\r\n\r\nFollowing Values are implemented:\r\n\r\n" + "|ChannelName|Unit|\r\n" + + "|---|---|\r\n"); + devices.append("* [" + thingDoc.getTitle() + "](" + + Paths.get(thingDoc.getClazz().getName().replaceAll("io.openems.impl.device.", "") + .replaceAll("[^\\.]*$", "").replace(".", "/"), "Readme.md").toString() + .replace("\\", "/") + + ")\r\n"); + Thing thing = thingDoc.getClazz().getConstructor(String.class, Device.class).newInstance("", null); + if (thing instanceof ModbusDeviceNature) { + ((ModbusDeviceNature) thing).init(); + } + List channelDocs = new LinkedList<>(thingDoc.getChannelDocs()); + Collections.sort(channelDocs, new Comparator() { + + @Override + public int compare(ChannelDoc arg0, ChannelDoc arg1) { + return arg0.getName().compareTo(arg1.getName()); + } + }); + for (ChannelDoc channelDoc : channelDocs) { + Member member = channelDoc.getMember(); + try { + List channels = new ArrayList<>(); + if (member instanceof Method) { + if (((Method) member).getReturnType().isArray()) { + Channel[] ch = (Channel[]) ((Method) member).invoke(thing); + for (Channel c : ch) { + channels.add(c); + } + } else { + // It's a Method with ReturnType Channel + channels.add((Channel) ((Method) member).invoke(thing)); + } + } else if (member instanceof Field) { + // It's a Field with Type Channel + channels.add((Channel) ((Field) member).get(thing)); + } else { + continue; + } + if (channels.isEmpty()) { + System.out.println("Channel is returning null! Thing [" + thing.id() + "], Member [" + + member.getName() + "]"); + continue; + } + for (Channel channel : channels) { + if (channel != null) { + StringBuilder unit = new StringBuilder(); + if (channel instanceof ReadChannel) { + ReadChannel rchannel = ((ReadChannel) channel); + unit.append(rchannel.unitOptional()); + rchannel.getLabels().forEach((key, value) -> { + unit.append(key + ": " + value + "
        "); + }); + } + fw.append("|" + channel.id() + "|" + unit + "|\r\n"); + } + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + System.out.println("Unable to add Channel. Member [" + member.getName() + "]"); + } + } + } catch (NoSuchMethodException e) { + + } + } + for (FileWriter fw : files.values()) { + fw.close(); + } + devices.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} From 260b044603ccc20114c1193d39ad9cd863801259 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:10:32 +0100 Subject: [PATCH 131/156] Add FaultsAndWarningsTranspiler. It transpiles all ThingStateEnum files to a format that is readable by the UI, so that the UI can translate fault/warning codes to human-readable descriptions. --- .../tools/FaultsAndWarningsTranspiler.java | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 tools/src/tools/FaultsAndWarningsTranspiler.java diff --git a/tools/src/tools/FaultsAndWarningsTranspiler.java b/tools/src/tools/FaultsAndWarningsTranspiler.java new file mode 100644 index 00000000000..009e0dd8d06 --- /dev/null +++ b/tools/src/tools/FaultsAndWarningsTranspiler.java @@ -0,0 +1,117 @@ +package tools; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Set; + +import com.google.common.reflect.ClassPath; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; + +import io.openems.api.channel.thingstate.FaultEnum; +import io.openems.api.channel.thingstate.ThingStateEnum; +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.api.exception.ReflectionException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ThingStateInfo; +import io.openems.common.utils.JsonUtils; + +/** + * This tool transpiles OpenEMS Edge Fault and Warning Enums to TypeScript for + * usage in OpenEMS UI. + * + * @author stefan.feilmeier + * + */ +public class FaultsAndWarningsTranspiler { + + private final static Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + public static void main(String[] args) + throws OpenemsException, InstantiationException, IllegalAccessException, IOException { + Path outputFile = Paths.get(".", "..", "ui", "src", "app", "device", "overview", "state", "thingstates.ts"); + JsonObject j = getJson(); + writeTypeScriptFile(outputFile, j); + System.out.println("Wrote file " + outputFile.toAbsolutePath()); + } + + private static void writeTypeScriptFile(Path outputFile, JsonObject j) throws IOException { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(j); + String out = "interface ThingStates { [clazz: string]: { 'faults'?: { [id: number]: string }, 'warnings'?: { [id: number]: string } } };" + + "\n" + "\n" + "export const THING_STATES: ThingStates = " + json + ";"; + Files.write(outputFile, out.getBytes(DEFAULT_CHARSET)); + } + + private static JsonObject getJson() throws ReflectionException, OpenemsException { + JsonObject j = new JsonObject(); + for (Class clazz : getEnums()) { + ThingStateInfo annotation = clazz.getAnnotation(ThingStateInfo.class); + if (annotation == null) { + System.err.println("@ThingStateInfo is missing for Enum [" + clazz.getName() + "]"); + continue; + } + // Find existing Thing definition or create new one + for (Class thingClazz : annotation.reference()) { + String thingClassName = thingClazz.getName(); + JsonObject jThing = JsonUtils.getAsOptionalJsonObject(j, thingClassName).orElse(new JsonObject()); + + // Parse enum constants + ThingStateEnum[] enoms = clazz.getEnumConstants(); + JsonObject jState = new JsonObject(); + for (ThingStateEnum enom : enoms) { + String name = splitCamelCase(enom.toString()); + jState.addProperty(String.valueOf(enom.getValue()), name); + } + + // Is it Fault or Warning? + if (FaultEnum.class.isAssignableFrom(clazz)) { + jThing.add("faults", jState); + } else if (WarningEnum.class.isAssignableFrom(clazz)) { + jThing.add("warnings", jState); + } else { + throw new OpenemsException("Neither Fault nor Warning in Enum [" + clazz.getName() + "]"); + } + + j.add(thingClassName, jThing); + } + } + return j; + } + + @SuppressWarnings("unchecked") + private static Set> getEnums() throws ReflectionException { + String topLevelPackage = "io.openems.impl"; + Class searchClazz = ThingStateEnum.class; + Set> clazzes = new HashSet<>(); + try { + ClassPath classpath = ClassPath.from(ClassLoader.getSystemClassLoader()); + for (ClassPath.ClassInfo classInfo : classpath.getTopLevelClassesRecursive(topLevelPackage)) { + Class thisClazz = classInfo.load(); + if (searchClazz.isAssignableFrom(thisClazz)) { + clazzes.add((Class) thisClazz); + } + } + } catch (IllegalArgumentException | IOException e) { + throw new ReflectionException(e.getMessage()); + } + return clazzes; + } + + /** + * Source: + * https://stackoverflow.com/questions/2559759/how-do-i-convert-camelcase-into-human-readable-names-in-java + * + * @param s + * @return + */ + private static String splitCamelCase(String s) { + return s.replaceAll(String.format("%s|%s|%s", "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])", + "(?<=[A-Za-z])(?=[^A-Za-z])"), " "); + } +} From 8c2f1a27ab4e948cdb2902ff1b17dd9461256cb2 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:11:30 +0100 Subject: [PATCH 132/156] Add thingstates.ts - generated by FaultsAndWarningsTranspiler tool --- .../app/device/overview/state/thingstates.ts | 1214 +++++++++++++++++ 1 file changed, 1214 insertions(+) create mode 100644 ui/src/app/device/overview/state/thingstates.ts diff --git a/ui/src/app/device/overview/state/thingstates.ts b/ui/src/app/device/overview/state/thingstates.ts new file mode 100644 index 00000000000..a569b8358d9 --- /dev/null +++ b/ui/src/app/device/overview/state/thingstates.ts @@ -0,0 +1,1214 @@ +interface ThingStates { [clazz: string]: { 'faults'?: { [id: number]: string }, 'warnings'?: { [id: number]: string } } }; + +export const THING_STATES: ThingStates = { + "io.openems.impl.device.simulator.SimulatorAsymmetricEss": { + "faults": { + "0": "Simulated Fault" + }, + "warnings": { + "0": "Simulated Warning" + } + }, + "io.openems.impl.device.commercial.FeneconCommercialEss": { + "warnings": { + "0": "Emergency Stop", + "1": "Key Manual Stop", + "2": "Transformer Phase B Temperature Sensor Invalidation", + "4": "SD Memory Card Invalidation", + "5": "Inverter Communication Abnormity", + "6": "Battery Stack Communication Abnormity", + "7": "Multifunctional Ammeter Communication Abnormity", + "8": "Remote Communication Abnormity", + "9": "PVDC 1 Communication Abnormity", + "10": "PVDC 2 Communication Abnormity", + "11": "Transformer Severe Overtemperature", + "12": "DC Precharge Contactor Inspection Abnormity", + "13": "DC Breaker 1 Inspection Abnormity", + "14": "DC Breaker 2 Inspection Abnormity", + "15": "AC Precharge Contactor Inspection Abnormity", + "16": "AC Mainontactor Inspection Abnormity", + "17": "AC Breaker Inspection Abnormity", + "18": "DC Breaker 1 Close Unsuccessfully", + "19": "DC Breaker 2 Close Unsuccessfully", + "20": "Control Signal Close Abnormally Inspected By System", + "21": "Control Signal Open Abnormally Inspected By System", + "22": "Neutral Wire Contactor Close Unsuccessfully", + "23": "Neutral Wire Contactor Open Unsuccessfully", + "24": "Work Door Open", + "25": "Emergency 1 Stop", + "26": "AC Breaker Close Unsuccessfully", + "27": "Control Switch Stop", + "28": "General Overload", + "29": "Severe Overload", + "30": "Battery Current Over Limit", + "31": "Power Decrease Caused By Overtemperature", + "32": "Inverter General Overtemperature", + "33": "AC Three Phase Current Unbalance", + "34": "Restore Factory Setting Unsuccessfully", + "35": "Pole Board Invalidation", + "36": "Self Inspection Failed", + "37": "Receive BMS Fault And Stop", + "38": "Refrigeration Equipmentinvalidation", + "39": "Large Temperature Difference Among IGBT Three Phases", + "40": "EEPROM Parameters Over Range", + "41": "EEPROM Parameters Backup Failed", + "42": "DC Breaker Closeunsuccessfully", + "43": "Communication Between Inverter And BSMU Disconnected", + "44": "Communication Between Inverter And Master Disconnected", + "45": "Communication Between Inverter And UC Disconnected", + "46": "BMS Start Overtime Controlled By PCS", + "47": "BMS Stop Overtime Controlled By PCS", + "48": "Sync Signal Invalidation", + "49": "Sync Signal Continuous Caputure Fault", + "50": "Sync Signal Several Times Caputure Fault" + }, + "faults": { + "0": "DC Precharge Contactor Close Unsuccessfully", + "1": "AC Precharge Contactor Close Unsuccessfully", + "2": "AC Main Contactor Close Unsuccessfully", + "3": "DC Electrical Breaker 1 Close Unsuccessfully", + "4": "DC Main Contactor Close Unsuccessfully", + "5": "AC Breaker Trip", + "6": "AC Main Contactor Open When Running", + "7": "DC Main Contactor Open When Running", + "8": "AC Main Contactor Open Unsuccessfully", + "9": "DC Electrical Breaker 1 Open Unsuccessfully", + "10": "DC Main Contactor Open Unsuccessfully", + "11": "Hardware PDP Fault", + "12": "Master Stop Suddenly", + "13": "DC Short Circuit Protection", + "14": "DC Overvoltage Protection", + "15": "DC Undervoltage Protection", + "16": "DC Inverse No Connection Protection", + "17": "DC Disconnection Protection", + "18": "Commuting Voltage Abnormity Protection", + "19": "DC Overcurrent Protection", + "20": "Phase 1 Peak Current Over Limit Protection", + "21": "Phase 2 Peak Current Over Limit Protection", + "22": "Phase 3 Peak Current Over Limit Protection", + "23": "Phase 1 Grid Voltage Sampling Invalidation", + "24": "Phase 2 Virtual Current Over Limit Protection", + "25": "Phase 3 Virtual Current Over Limit Protection", + "26": "Phase 1 Grid Voltage Sampling Invalidation 2", + "27": "Phase 2rid Voltage Sampling Invalidation", + "28": "Phase 3 Grid Voltage Sampling Invalidation", + "29": "Phase 1 Invert Voltage Sampling Invalidation", + "30": "Phase 2 Invert Voltage Sampling Invalidation", + "31": "Phase 3 Invert Voltage Sampling Invalidation", + "32": "AC Current Sampling Invalidation", + "33": "DC Current Sampling Invalidation", + "34": "Phase 1 Overtemperature Protection", + "35": "Phase 2 Overtemperature Protection", + "36": "Phase 3 Overtemperature Protection", + "37": "Phase 1 Temperature Sampling Invalidation", + "38": "Phase 2 Temperature Sampling Invalidation", + "39": "Phase 3 Temperature Sampling Invalidation", + "40": "Phase 1 Precharge Unmet Protection", + "41": "Phase 2 Precharge Unmet Protection", + "42": "Phase 3 Precharge Unmet Protection", + "43": "Unadaptable Phase Sequence Error Protection", + "44": "DSP Protection", + "45": "Phase 1 Grid Voltage Severe Overvoltage Protection", + "46": "Phase 1 Grid Voltage General Overvoltage Protection", + "47": "Phase 2 Grid Voltage Severe Overvoltage Protection", + "48": "Phase 2 Grid Voltage General Overvoltage Protection", + "49": "Phase 3 Grid Voltage Severe Overvoltage Protection", + "50": "Phase 3 Grid Voltage General Overvoltage Protection", + "51": "Phase 1 Grid Voltage Severe Undervoltage Protection", + "52": "Phase 1 Grid Voltage General Undervoltage Protection", + "53": "Phase 2 Grid Voltage Severe Undervoltage Protection", + "54": "Phase 2 Grid Voltage General Undervoltage Protection", + "55": "Phase 3 Grid Voltage Severe Undervoltage Protection", + "56": "Phase 3 Grid Voltage General Undervoltage Protection", + "57": "Severe Overfrequncy Protection", + "58": "General Overfrequncy Protection", + "59": "Severe Underfrequncy Protection", + "60": "Generals Underfrequncy Protection", + "61": "Phase 1 Gridloss", + "62": "Phase 2 Gridloss", + "63": "Phase 3 Gridloss", + "64": "Islanding Protection", + "65": "Phase 1 Under Voltage Ride Through", + "66": "Phase 2 Under Voltage Ride Through", + "67": "Phase 3 Under Voltage Ride Through", + "68": "Phase 1 Inverter Voltage Severe Overvoltage Protection", + "69": "Phase 1 Inverter Voltage General Overvoltage Protection", + "70": "Phase 2 Inverter Voltage Severe Overvoltage Protection", + "71": "Phase 2 Inverter Voltage General Overvoltage Protection", + "72": "Phase 3 Inverter Voltage Severe Overvoltage Protection", + "73": "Phase 3 Inverter Voltage General Overvoltage Protection", + "74": "Inverter Peak Voltage High Protection Cause By AC Disconnect" + } + }, + "io.openems.impl.device.commercial.FeneconCommercialCharger": { + "warnings": { + "0": "Current Sampling Channel Abnormity On High Voltage Side", + "1": "Current Sampling Channel Abnormity On Low Voltage Side", + "2": "Bms DCDC 1 EEPROM Parameters Over Range", + "3": "EEPROM Parameters Over Range", + "4": "Update EEPROM Failed", + "5": "Read EEPROM Failed", + "6": "Current Sampling Channel Abnormity Before Inductance", + "7": "Reactor Power Decrease Caused By Overtemperature", + "8": "IGBT Power Decrease Caused By Overtemperature", + "9": "Temperature Chanel 3 Power Decrease Caused By Overtemperature", + "10": "Temperature Chanel 4 Power Decrease Caused By Overtemperature", + "11": "Temperature Chanel 5 Power Decrease Caused By Overtemperature", + "12": "Temperature Chanel 6 Power Decrease Caused By Overtemperature", + "13": "Temperature Chanel 7 Power Decrease Caused By Overtemperature", + "14": "Temperature Chanel 8 Power Decrease Caused By Overtemperature", + "15": "Fan 1 Stop Failed", + "16": "Fan 2 Stop Failed", + "17": "Fan 3 Stop Failed", + "18": "Fan 4 Stop Failed", + "19": "Fan 1 Startup Failed", + "20": "Fan 2 Startup Failed", + "21": "Fan 3 Startup Failed", + "22": "Fan 4 Startup Failed", + "23": "High Voltage Side Overvoltage", + "24": "High Voltage Side Undervoltage", + "25": "High Voltage Side Voltage Change Unconventionally", + "26": "Current Abnormity Before DC Converter Work On High Voltage Side", + "27": "Current Abnormity Before DC Converter Work On Low Voltage S Xide", + "28": "Initial Duty Ratio Abnormity Before DC Converter Work", + "29": "Voltage Abnormity Before DC Converter Work On High Voltage Side", + "30": "Voltage Abnormity Before DC Converter Work On Low Voltage Side", + "31": "High Voltage Breaker Inspection Abnormity", + "32": "Low Voltage Breaker Inspection Abnormity", + "33": "Bsm DCDC 5 DC Precharge Contactor Inspection Abnormity", + "34": "DC Precharge Contactor Open Unsuccessfully", + "35": "DC Main Contactor Inspection Abnormity", + "36": "DC Main Contactor Open Unsuccessfully", + "37": "Output Contactor Close Unsuccessfully", + "38": "Output Contactor Open Unsuccessfully", + "39": "AC Main Contactor Close Unsuccessfully", + "40": "AC Main Contactor Open Unsuccessfully", + "41": "Neg Contactor Open Unsuccessfully", + "42": "Neg Contactor Close Unsuccessfully", + "43": "Neg Contactor State Abnormal", + "44": "Bsm DCDC 1 Current Sampling Channel Abnormity On High Voltage Side", + "45": "Bsm DCDC 1 Current Sampling Channel Abnormity On Low Voltage Side", + "46": "Bsm DCDC 1 EEPROM Parameters Over Range", + "47": "Bsm DCDC 1 Update EEPROM Failed", + "48": "Bsm DCDC 1 Read EEPROM Failed", + "49": "Bsm DCDC 1 Current Sampling Channel Abnormity Before Inductance", + "50": "Bsm DCDC 1 Reactor Power Decrease Caused By Overtemperature", + "51": "Bsm DCDC 1 IGBT Power Decrease Caused By Overtemperature", + "52": "Bsm DCDC 1 Temperature Chanel 3 Power Decrease Caused By Overtemperature", + "53": "Bsm DCDC 1 Temperature Chanel 4 Power Decrease Caused By Overtemperature", + "54": "Bsm DCDC 1 Temperature Chanel 5 Power Decrease Caused By Overtemperature", + "55": "Bsm DCDC 1 Temperature Chanel 6 Power Decrease Caused By Overtemperature", + "56": "Bsm DCDC 1 Temperature Chanel 7 Power Decrease Caused By Overtemperature", + "57": "Bsm DCDC 1 Temperature Chanel 8 Power Decrease Caused By Overtemperature", + "58": "Bsm DCDC 1 Fan 1 Stop Failed", + "59": "Bsm DCDC 1 Fan 2 Stop Failed", + "60": "Bsm DCDC 1 Fan 3 Stop Failed", + "61": "Bsm DCDC 1 Fan 4 Stop Failed", + "62": "Bsm DCDC 1 Fan 1 Startup Failed", + "63": "Bsm DCDC 1 Fan 2 Startup Failed", + "64": "Bsm DCDC 1 Fan 3 Startup Failed", + "65": "Bsm DCDC 1 Fan 4 Startup Failed", + "66": "Bsm DCDC 1 High Voltage Side Overvoltage", + "67": "Bsm DCDC 1 High Voltage Side Undervoltage", + "68": "Bsm DCDC 1 High Voltage Side Voltage Change Unconventionally", + "69": "Bms DCDC 1 Current Abnormity Before DC Converter Work On High Voltage Side", + "70": "Bms DCDC 1 Current Abnormity Before DC Converter Work On Low Voltage S Xide", + "71": "Bms DCDC 1 Initial Duty Ratio Abnormity Before DC Converter Work", + "72": "Bms DCDC 1 Voltage Abnormity Before DC Converter Work On High Voltage Side", + "73": "Bms DCDC 1 Voltage Abnormity Before DC Converter Work On Low Voltage Side", + "74": "Bms DCDC 1 High Voltage Breaker Inspection Abnormity", + "75": "Bms DCDC 1 Low Voltage Breaker Inspection Abnormity", + "76": "Bms DCDC 1 Bsm DCDC 5 DC Precharge Contactor Inspection Abnormity", + "77": "Bms DCDC 1 DC Precharge Contactor Open Unsuccessfully", + "78": "Bms DCDC 1 DC Main Contactor Inspection Abnormity", + "79": "Bms DCDC 1 DC Main Contactor Open Unsuccessfully", + "80": "Bms DCDC 1 Output Contactor Close Unsuccessfully", + "81": "Bms DCDC 1 Output Contactor Open Unsuccessfully", + "82": "Bms DCDC 1 AC Main Contactor Close Unsuccessfully", + "83": "Bms DCDC 1 AC Main Contactor Open Unsuccessfully", + "84": "Bms DCDC 1 Neg Contactor Open Unsuccessfully", + "85": "Bms DCDC 1 Neg Contactor Close Unsuccessfully", + "86": "Bms DCDC 1 Neg Contactor State Abnormal", + "87": "Pv DCDC Current Sampling Channel Abnormity On High Voltage Side", + "88": "Pv DCDC Current Sampling Channel Abnormity On Low Voltage Side", + "89": "Pv DCDCEEPROM Parameters Over Range", + "90": "Pv DCDC Update EEPROM Failed", + "91": "Pv DCDC Read EEPROM Failed", + "92": "Pv DCDC Current Sampling Channel Abnormity Before Inductance", + "93": "Pv DCDC Reactor Power Decrease Caused By Overtemperature", + "94": "Pv DCDCIGBT Power Decrease Caused By Overtemperature", + "95": "Pv DCDC Temperature Chanel 3 Power Decrease Caused By Overtemperature", + "96": "Pv DCDC Temperature Chanel 4 Power Decrease Caused By Overtemperature", + "97": "Pv DCDC Temperature Chanel 5 Power Decrease Caused By Overtemperature", + "98": "Pv DCDC Temperature Chanel 6 Power Decrease Caused By Overtemperature", + "99": "Pv DCDC Temperature Chanel 7 Power Decrease Caused By Overtemperature", + "100": "Pv DCDC Temperature Chanel 8 Power Decrease Caused By Overtemperature", + "101": "Pv DCDC Fan 1 Stop Failed", + "102": "Pv DCDC Fan 2 Stop Failed", + "103": "Pv DCDC Fan 3 Stop Failed", + "104": "Pv DCDC Fan 4 Stop Failed", + "105": "Pv DCDC Fan 1 Startup Failed", + "106": "Pv DCDC Fan 2 Startup Failed", + "107": "Pv DCDC Fan 3 Startup Failed", + "108": "Pv DCDC Fan 4 Startup Failed", + "109": "Pv DCDC High Voltage Side Overvoltage", + "110": "Pv DCDC High Voltage Side Undervoltage", + "111": "Pv DCDC High Voltage Side Voltage Change Unconventionally", + "112": "Pv DCDC Current Abnormity Before DC Converter Work On High Voltage Side", + "113": "Pv DCDC Current Abnormity Before DC Converter Work On Low Voltage S Xide", + "114": "Pv DCDC Initial Duty Ratio Abnormity Before DC Converter Work", + "115": "Pv DCDC Voltage Abnormity Before DC Converter Work On High Voltage Side", + "116": "Pv DCDC Voltage Abnormity Before DC Converter Work On Low Voltage Side", + "117": "Pv DCDC High Voltage Breaker Inspection Abnormity", + "118": "Pv DCDC Low Voltage Breaker Inspection Abnormity", + "119": "Pv DCDC Bsm DCDC 5 DC Precharge Contactor Inspection Abnormity", + "120": "Pv DCDCDC Precharge Contactor Open Unsuccessfully", + "121": "Pv DCDCDC Main Contactor Inspection Abnormity", + "122": "Pv DCDCDC Main Contactor Open Unsuccessfully", + "123": "Pv DCDC Output Contactor Close Unsuccessfully", + "124": "Pv DCDC Output Contactor Open Unsuccessfully", + "125": "Pv DCDCAC Main Contactor Close Unsuccessfully", + "126": "Pv DCDCAC Main Contactor Open Unsuccessfully", + "127": "Pv DCDC Neg Contactor Open Unsuccessfully", + "128": "Pv DCDC Neg Contactor Close Unsuccessfully", + "129": "Pv DCDC Neg Contactor State Abnormal", + "130": "Pv DCDC 1 Current Sampling Channel Abnormity On High Voltage Side", + "131": "Pv DCDC 1 Current Sampling Channel Abnormity On Low Voltage Side", + "132": "Pv DCDC 1 EEPROM Parameters Over Range", + "133": "Pv DCDC 1 Update EEPROM Failed", + "134": "Pv DCDC 1 Read EEPROM Failed", + "135": "Pv DCDC 1 Current Sampling Channel Abnormity Before Inductance", + "136": "Pv DCDC 1 Reactor Power Decrease Caused By Overtemperature", + "137": "Pv DCDC 1 IGBT Power Decrease Caused By Overtemperature", + "138": "Pv DCDC 1 Temperature Chanel 3 Power Decrease Caused By Overtemperature", + "139": "Pv DCDC 1 Temperature Chanel 4 Power Decrease Caused By Overtemperature", + "140": "Pv DCDC 1 Temperature Chanel 5 Power Decrease Caused By Overtemperature", + "141": "Pv DCDC 1 Temperature Chanel 6 Power Decrease Caused By Overtemperature", + "142": "Pv DCDC 1 Temperature Chanel 7 Power Decrease Caused By Overtemperature", + "143": "Pv DCDC 1 Temperature Chanel 8 Power Decrease Caused By Overtemperature", + "144": "Pv DCDC 1 Fan 1 Stop Failed", + "145": "Pv DCDC 1 Fan 2 Stop Failed", + "146": "Pv DCDC 1 Fan 3 Stop Failed", + "147": "Pv DCDC 1 Fan 4 Stop Failed", + "148": "Pv DCDC 1 Fan 1 Startup Failed", + "149": "Pv DCDC 1 Fan 2 Startup Failed", + "150": "Pv DCDC 1 Fan 3 Startup Failed", + "151": "Pv DCDC 1 Fan 4 Startup Failed", + "152": "Pv DCDC 1 High Voltage Side Overvoltage", + "153": "Pv DCDC 1 High Voltage Side Undervoltage", + "154": "Pv DCDC 1 High Voltage Side Voltage Change Unconventionally", + "155": "Pv DCDC 1 Current Abnormity Before DC Converter Work On High Voltage Side", + "156": "Pv DCDC 1 Current Abnormity Before DC Converter Work On Low Voltage S Xide", + "157": "Pv DCDC 1 Initial Duty Ratio Abnormity Before DC Converter Work", + "158": "Pv DCDC 1 Voltage Abnormity Before DC Converter Work On High Voltage Side", + "159": "Pv DCDC 1 Voltage Abnormity Before DC Converter Work On Low Voltage Side", + "160": "Pv DCDC 1 High Voltage Breaker Inspection Abnormity", + "161": "Pv DCDC 1 Low Voltage Breaker Inspection Abnormity", + "162": "Pv DCDC 1 Bsm DCDC 5 DC Precharge Contactor Inspection Abnormity", + "163": "Pv DCDC 1 DC Precharge Contactor Open Unsuccessfully", + "164": "Pv DCDC 1 DC Main Contactor Inspection Abnormity", + "165": "Pv DCDC 1 DC Main Contactor Open Unsuccessfully", + "166": "Pv DCDC 1 Output Contactor Close Unsuccessfully", + "167": "Pv DCDC 1 Output Contactor Open Unsuccessfully", + "168": "Pv DCDC 1 AC Main Contactor Close Unsuccessfully", + "169": "Pv DCDC 1 AC Main Contactor Open Unsuccessfully", + "170": "Pv DCDC 1 Neg Contactor Open Unsuccessfully", + "171": "Pv DCDC 1 Neg Contactor Close Unsuccessfully", + "172": "Pv DCDC 1 Neg Contactor State Abnormal" + }, + "faults": { + "0": "High Voltage Side Of DC Converter Undervoltage", + "1": "High Voltage Side Of DC Converter Overvoltage", + "2": "Low Voltage Side Of DC Converter Undervoltage", + "3": "Low Voltage Side Of DC Converter Overvoltage", + "4": "High Voltage Side Of DC Converter Overcurrent Fault", + "5": "Low Voltage Side Of DC Converter Overcurrent Fault", + "6": "DC Converter IGBT Fault", + "7": "DC Converter Precharge Unmet", + "8": "BECU Communication Disconnected", + "9": "DC Converter Communication Disconnected", + "10": "Current Configuration Over Range", + "11": "The Battery Request Stop", + "12": "Overcurrent Relay Fault", + "13": "Lightning Protection Device Fault", + "14": "DC Converter Priamary Contactor Disconnected Abnormally", + "15": "DC Disconnected Abnormally On Low Voltage Side Of DC Convetor", + "16": "DC Convetor EEPROM Abnormity 1", + "17": "DC Convetor EEPROM Abnormity 1 Second", + "18": "EDC Convetor EEPROM Abnormity 1", + "19": "DC Convertor General Overload", + "20": "DC Short Circuit", + "21": "Peak Pulse Current Protection", + "22": "DC Disconnect Abnormally On High Voltage Side Of DC Convetor", + "23": "Effective Pulse Value Overhigh", + "24": "DC Converte Severe Overload", + "25": "DC Breaker Disconnect Abnormally On High Voltage Side Of DC Convetor", + "26": "DC Breaker Disconnect Abnormally On Low Voltage Side Of DC Convetor", + "27": "DC Convetor Precharge Contactor Close Failed", + "28": "DC Convetor Main Contactor Close Failed", + "29": "AC Contactor State Abnormity Of DC Convetor", + "30": "DC Convetor Emergency Stop", + "31": "DC Converter Charging Gun Disconnected", + "32": "DC Current Abnormity Before DC Convetor Work", + "33": "Fu Se Disconnected", + "34": "DC Converter Hardware Current Or Voltage Fault", + "35": "DC Converter Crystal Oscillator Circuit Invalidation", + "36": "DC Converter Reset Circuit Invalidation", + "37": "DC Converter Sampling Circuit Invalidation", + "38": "DC Converter Digital IO Circuit Invalidation", + "39": "DC Converter PWM Circuit Invalidation", + "40": "DC Converter X 5045 Circuit Invalidation", + "41": "DC Converter CAN Circuit Invalidation", + "42": "DC Converter Software AND Hardware Protection Circuit Invalidation", + "43": "DC Converter Power Circuit Invalidation", + "44": "DC Converter CPU Invalidation", + "45": "DC Converter TINT 0 Interrupt Invalidation", + "46": "DC Converter ADC Interrupt Invalidation", + "47": "DC Converter CAPITN 4 Interrupt Invalidation", + "48": "DC Converter CAPINT 6 Interrupt Invalidation", + "49": "DC Converter T 3 PIN Tinterrupt Invalidation", + "50": "DC Converter T 4 PIN Tinterrupt Invalidation", + "51": "DC Converter PDPINTA Interrupt Invalidation", + "52": "DC Converter T 1 PINT Interrupt Invalidation", + "53": "DC Converter RESV Interrupt Invalidation", + "54": "DC Converter 100us Task Invalidation", + "55": "DC Converter Clock Invalidation", + "56": "DC Converter EMS Memory Invalidation", + "57": "DC Converter Exterior Communication Invalidation", + "58": "DC Converter IO Interface Invalidation", + "59": "DC Converter Input Voltage Bound Fault", + "60": "DC Converter Outter Voltage Bound Fault", + "61": "DC Converter Output Voltage Bound Fault", + "62": "DC Converter Induct Current Bound Fault", + "63": "DC Converter Input Current Bound Fault", + "64": "DC Converter Output Current Bound Fault", + "65": "DC Reactor Over Temperature", + "66": "DCIGBT Over Temperature", + "67": "DC Converter Chanel 3 Over Temperature", + "68": "DC Converter Chanel 4 Over Temperature", + "69": "DC Converter Chanel 5 Over Temperature", + "70": "DC Converter Chanel 6 Over Temperature", + "71": "DC Converter Chanel 7 Over Temperature", + "72": "DC Converter Chanel 8 Over Temperature", + "73": "DC Reactor Temperature Sampling Invalidation", + "74": "DCIGBT Temperature Sampling Invalidation", + "75": "DC Converter Chanel 3 Temperature Sampling Invalidation", + "76": "DC Converter Chanel 4 Temperature Sampling Invalidation", + "77": "DC Converter Chanel 5 Temperature Sampling Invalidation", + "78": "DC Converter Chanel 6 Temperature Sampling Invalidation", + "79": "DC Converter Chanel 7 Temperature Sampling Invalidation", + "80": "DC Converter Chanel 8 Temperature Sampling Invalidation", + "81": "DC Converter Inductance Current Sampling Invalidation", + "82": "Current Sampling Invalidation On The Low Voltage Side Of DC Converter", + "83": "Voltage Sampling Invalidation On The Low Voltage Side Of DC Converter", + "84": "Insulation Inspection Fault", + "85": "Neg Contactor Close Unsuccessly", + "86": "Neg Contactor Cut When Running", + "87": "Bms DCDC 1 High Voltage Side Of DC Converter Undervoltage", + "88": "Bms DCDC 1 High Voltage Side Of DC Converter Overvoltage", + "89": "Bms DCDC 1 Low Voltage Side Of DC Converter Undervoltage", + "90": "Bms DCDC 1 Low Voltage Side Of DC Converter Overvoltage", + "91": "Bms DCDC 1 High Voltage Side Of DC Converter Overcurrent Fault", + "92": "Bms DCDC 1 Low Voltage Side Of DC Converter Overcurrent Fault", + "93": "Bms DCDC 1 DC Converter IGBT Fault", + "94": "Bms DCDC 1 DC Converter Precharge Unmet", + "95": "Bms DCDC 1 BECU Communication Disconnected", + "96": "Bms DCDC 1 DC Converter Communication Disconnected", + "97": "Bms DCDC 1 Current Configuration Over Range", + "98": "Bms DCDC 1 The Battery Request Stop", + "99": "Bms DCDC 1 Overcurrent Relay Fault", + "100": "Bms DCDC 1 Lightning Protection Device Fault", + "101": "Bms DCDC 1 DC Converter Priamary Contactor Disconnected Abnormally", + "102": "Bms DCDC 1 DC Disconnected Abnormally On Low Voltage Side Of DC Convetor", + "103": "Bms DCDC 1 DC Convetor EEPROM Abnormity 1", + "104": "Bms DCDC 1 DC Convetor EEPROM Abnormity 1 Second", + "105": "Bms DCDC 1 EDC Convetor EEPROM Abnormity 1", + "106": "Bsm DCDC 1 DC Convertor General Overload", + "107": "Bsm DCDC 1 DC Short Circuit", + "108": "Bsm DCDC 1 Peak Pulse Current Protection", + "109": "Bsm DCDC 1 DC Disconnect Abnormally On High Voltage Side Of DC Convetor", + "110": "Bsm DCDC 1 Effective Pulse Value Overhigh", + "111": "Bsm DCDC 1 DC Converte Severe Overload", + "112": "Bsm DCDC 1 DC Breaker Disconnect Abnormally On High Voltage Side Of DC Convetor", + "113": "Bsm DCDC 1 DC Breaker Disconnect Abnormally On Low Voltage Side Of DC Convetor", + "114": "Bsm DCDC 1 DC Convetor Precharge Contactor Close Failed", + "115": "Bsm DCDC 1 DC Convetor Main Contactor Close Failed", + "116": "Bsm DCDC 1 AC Contactor State Abnormity Of DC Convetor", + "117": "Bsm DCDC 1 DC Convetor Emergency Stop", + "118": "Bsm DCDC 1 DC Converter Charging Gun Disconnected", + "119": "Bsm DCDC 1 DC Current Abnormity Before DC Convetor Work", + "120": "Bsm DCDC 1 Fu Se Disconnected", + "121": "Bsm DCDC 1 DC Converter Hardware Current Or Voltage Fault", + "122": "Bms DCDC 1 DC Converter Crystal Oscillator Circuit Invalidation", + "123": "Bms DCDC 1 DC Converter Reset Circuit Invalidation", + "124": "Bms DCDC 1 DC Converter Sampling Circuit Invalidation", + "125": "Bms DCDC 1 DC Converter Digital IO Circuit Invalidation", + "126": "Bms DCDC 1 DC Converter PWM Circuit Invalidation", + "127": "Bms DCDC 1 DC Converter X 5045 Circuit Invalidation", + "128": "Bms DCDC 1 DC Converter CAN Circuit Invalidation", + "129": "Bms DCDC 1 DC Converter Software AND Hardware Protection Circuit Invalidation", + "130": "Bms DCDC 1 DC Converter Power Circuit Invalidation", + "131": "Bms DCDC 1 DC Converter CPU Invalidation", + "132": "Bms DCDC 1 DC Converter TINT 0 Interrupt Invalidation", + "133": "Bms DCDC 1 DC Converter ADC Interrupt Invalidation", + "134": "Bms DCDC 1 DC Converter CAPITN 4 Interrupt Invalidation", + "135": "Bms DCDC 1 DC Converter CAPINT 6 Interrupt Invalidation", + "136": "Bms DCDC 1 DC Converter T 3 PIN Tinterrupt Invalidation", + "137": "Bms DCDC 1 DC Converter T 4 PIN Tinterrupt Invalidation", + "138": "Bms DCDC 1 DC Converter PDPINTA Interrupt Invalidation", + "139": "Bms DCDC 1 DC Converter T 1 PINT Interrupt Invalidation Second", + "140": "Bms DCDC 1 DC Converter RESV Interrupt Invalidation", + "141": "Bms DCDC 1 DC Converter 100us Task Invalidation", + "142": "Bms DCDC 1 DC Converter Clock Invalidation", + "143": "Bms DCDC 1 DC Converter EMS Memory Invalidation", + "144": "Bms DCDC 1 DC Converter Exterior Communication Invalidation", + "145": "Bms DCDC 1 DC Converter IO Interface Invalidation", + "146": "Bms DCDC 1 DC Converter Input Voltage Bound Fault", + "147": "Bms DCDC 1 DC Converter Outter Voltage Bound Fault", + "148": "Bms DCDC 1 DC Converter Output Voltage Bound Fault", + "149": "Bms DCDC 1 DC Converter Induct Current Bound Fault", + "150": "Bms DCDC 1 DC Converter Input Current Bound Fault", + "151": "Bms DCDC 1 DC Converter Output Current Bound Fault", + "152": "Bms DCDC 1 DC Reactor Over Temperature", + "153": "Bms DCDC 1 DCIGBT Over Temperature", + "154": "Bms DCDC 1 DC Converter Chanel 3 Over Temperature", + "155": "Bms DCDC 1 DC Converter Chanel 4 Over Temperature", + "156": "Bms DCDC 1 DC Converter Chanel 5 Over Temperature", + "157": "Bms DCDC 1 DC Converter Chanel 6 Over Temperature", + "158": "Bms DCDC 1 DC Converter Chanel 7 Over Temperature", + "159": "Bms DCDC 1 DC Converter Chanel 8 Over Temperature", + "160": "Bms DCDC 1 DC Reactor Temperature Sampling Invalidation", + "161": "Bms DCDC 1 DCIGBT Temperature Sampling Invalidation", + "162": "Bms DCDC 1 DC Converter Chanel 3 Temperature Sampling Invalidation", + "163": "Bms DCDC 1 DC Converter Chanel 4 Temperature Sampling Invalidation", + "164": "Bms DCDC 1 DC Converter Chanel 5 Temperature Sampling Invalidation", + "165": "Bms DCDC 1 DC Converter Chanel 6 Temperature Sampling Invalidation", + "166": "Bms DCDC 1 DC Converter Chanel 7 Temperature Sampling Invalidation", + "167": "Bms DCDC 1 DC Converter Chanel 8 Temperature Sampling Invalidation", + "168": "Bms DCDC 1 DC Converter Inductance Current Sampling Invalidation", + "169": "Bms DCDC 1 Current Sampling Invalidation On The Low Voltage Side Of DC Converter", + "170": "Bms DCDC 1 Voltage Sampling Invalidation On The Low Voltage Side Of DC Converter", + "171": "Bms DCDC 1 Insulation Inspection Fault", + "172": "Bms DCDC 1 Neg Contactor Close Unsuccessly", + "173": "Bms DCDC 1 Neg Contactor Cut When Running", + "174": "Pv DCDC High Voltage Side Of DC Converter Undervoltage", + "175": "Pv DCDC High Voltage Side Of DC Converter Overvoltage", + "176": "Pv DCDC Low Voltage Side Of DC Converter Undervoltage", + "177": "Pv DCDC Low Voltage Side Of DC Converter Overvoltage", + "178": "Pv DCDC High Voltage Side Of DC Converter Overcurrent Fault", + "179": "Pv DCDC Low Voltage Side Of DC Converter Overcurrent Fault", + "180": "Pv DCDCDC Converter IGBT Fault", + "181": "Pv DCDCDC Converter Precharge Unmet", + "182": "Pv DCDCBECU Communication Disconnected", + "183": "Pv DCDCDC Converter Communication Disconnected", + "184": "Pv DCDC Current Configuration Over Range", + "185": "Pv DCDC The Battery Request Stop", + "186": "Pv DCDC Overcurrent Relay Fault", + "187": "Pv DCDC Lightning Protection Device Fault", + "188": "Pv DCDCDC Converter Priamary Contactor Disconnected Abnormally", + "189": "Pv DCDCDC Disconnected Abnormally On Low Voltage Side Of DC Convetor", + "190": "Pv DCDCDC Convetor EEPROM Abnormity 1", + "191": "Pv DCDCDC Convetor EEPROM Abnormity 1 Second", + "192": "Pv DCDCEDC Convetor EEPROM Abnormity 1", + "193": "Pv DCDCDC Convertor General Overload", + "194": "Pv DCDCDC Short Circuit", + "195": "Pv DCDC Peak Pulse Current Protection", + "196": "Pv DCDCDC Disconnect Abnormally On High Voltage Side Of DC Convetor", + "197": "Pv DCDC Effective Pulse Value Overhigh", + "198": "Pv DCDCDC Converte Severe Overload", + "199": "Pv DCDCDC Breaker Disconnect Abnormally On High Voltage Side Of DC Convetor", + "200": "Pv DCDCDC Breaker Disconnect Abnormally On Low Voltage Side Of DC Convetor", + "201": "Pv DCDCDC Convetor Precharge Contactor Close Failed", + "202": "Pv DCDCDC Convetor Main Contactor Close Failed", + "203": "Pv DCDCAC Contactor State Abnormity Of DC Convetor", + "204": "Pv DCDCDC Convetor Emergency Stop", + "205": "Pv DCDCDC Converter Charging Gun Disconnected", + "206": "Pv DCDCDC Current Abnormity Before DC Convetor Work", + "207": "Pv DCDC Fu Se Disconnected", + "208": "Pv DCDCDC Converter Hardware Current Or Voltage Fault", + "209": "Pv DCDCDC Converter Crystal Oscillator Circuit Invalidation", + "210": "Pv DCDCDC Converter Reset Circuit Invalidation", + "211": "Pv DCDCDC Converter Sampling Circuit Invalidation", + "212": "Pv DCDCDC Converter Digital IO Circuit Invalidation", + "213": "Pv DCDCDC Converter PWM Circuit Invalidation", + "214": "Pv DCDCDC Converter X 5045 Circuit Invalidation", + "215": "Pv DCDCDC Converter CAN Circuit Invalidation", + "216": "Pv DCDCDC Converter Software AND Hardware Protection Circuit Invalidation", + "217": "Pv DCDCDC Converter Power Circuit Invalidation", + "218": "Pv DCDCDC Converter CPU Invalidation", + "219": "Pv DCDCDC Converter TINT 0 Interrupt Invalidation", + "220": "Pv DCDCDC Converter ADC Interrupt Invalidation", + "221": "Pv DCDCDC Converter CAPITN 4 Interrupt Invalidation", + "222": "Pv DCDCDC Converter CAPINT 6 Interrupt Invalidation", + "223": "Pv DCDCDC Converter T 3 PIN Tinterrupt Invalidation", + "224": "Pv DCDCDC Converter T 4 PIN Tinterrupt Invalidation", + "225": "Pv DCDC Converter PDPINTA Interrupt Invalidation", + "226": "Pv DCDC Converter T 1 PINT Interrupt Invalidation Second", + "227": "Pv DCDC Converter RESV Interrupt Invalidation", + "228": "Pv DCDC Converter 100us Task Invalidation", + "229": "Pv DCDC Converter Clock Invalidation", + "230": "Pv DCDC Converter EMS Memory Invalidation", + "231": "Pv DCDC Converter Exterior Communication Invalidation", + "232": "Pv DCDC Converter IO Interface Invalidation", + "233": "Pv DCDC Converter Input Voltage Bound Fault", + "234": "Pv DCDC Converter Outter Voltage Bound Fault", + "235": "Pv DCDC Converter Output Voltage Bound Fault", + "236": "Pv DCDC Converter Induct Current Bound Fault", + "237": "Pv DCDC Converter Input Current Bound Fault", + "238": "Pv DCDC Converter Output Current Bound Fault", + "239": "Pv DCDCDC Reactor Over Temperature", + "240": "Pv DCDCDCIGBT Over Temperature", + "241": "Pv DCDCDC Converter Chanel 3 Over Temperature", + "242": "Pv DCDCDC Converter Chanel 4 Over Temperature", + "243": "Pv DCDCDC Converter Chanel 5 Over Temperature", + "244": "Pv DCDCDC Converter Chanel 6 Over Temperature", + "245": "Pv DCDCDC Converter Chanel 7 Over Temperature", + "246": "Pv DCDCDC Converter Chanel 8 Over Temperature", + "247": "Pv DCDCDC Reactor Temperature Sampling Invalidation", + "248": "Pv DCDCDCIGBT Temperature Sampling Invalidation", + "249": "Pv DCDCDC Converter Chanel 3 Temperature Sampling Invalidation", + "250": "Pv DCDCDC Converter Chanel 4 Temperature Sampling Invalidation", + "251": "Pv DCDCDC Converter Chanel 5 Temperature Sampling Invalidation", + "252": "Pv DCDCDC Converter Chanel 6 Temperature Sampling Invalidation", + "253": "Pv DCDCDC Converter Chanel 7 Temperature Sampling Invalidation", + "254": "Pv DCDCDC Converter Chanel 8 Temperature Sampling Invalidation", + "255": "Pv DCDCDC Converter Inductance Current Sampling Invalidation", + "256": "Pv DCDC Current Sampling Invalidation On The Low Voltage Side Of DC Converter", + "257": "Pv DCDC Voltage Sampling Invalidation On The Low Voltage Side Of DC Converter", + "258": "Pv DCDC Insulation Inspection Fault", + "259": "Pv DCDC Neg Contactor Close Unsuccessly", + "260": "Pv DCDC Neg Contactor Cut When Running", + "261": "Pv DCDC 1 High Voltage Side Of DC Converter Undervoltage", + "262": "Pv DCDC 1 High Voltage Side Of DC Converter Overvoltage", + "263": "Pv DCDC 1 Low Voltage Side Of DC Converter Undervoltage", + "264": "Pv DCDC 1 Low Voltage Side Of DC Converter Overvoltage", + "265": "Pv DCDC 1 High Voltage Side Of DC Converter Overcurrent Fault", + "266": "Pv DCDC 1 Low Voltage Side Of DC Converter Overcurrent Fault", + "267": "Pv DCDC 1 DC Converter IGBT Fault", + "268": "Pv DCDC 1 DC Converter Precharge Unmet", + "269": "Pv DCDC 1 BECU Communication Disconnected", + "270": "Pv DCDC 1 DC Converter Communication Disconnected", + "271": "Pv DCDC 1 Current Configuration Over Range", + "272": "Pv DCDC 1 The Battery Request Stop", + "273": "Pv DCDC 1 Overcurrent Relay Fault", + "274": "Pv DCDC 1 Lightning Protection Device Fault", + "275": "Pv DCDC 1 DC Converter Priamary Contactor Disconnected Abnormally", + "276": "Pv DCDC 1 DC Disconnected Abnormally On Low Voltage Side Of DC Convetor", + "277": "Pv DCDC 1 DC Convetor EEPROM Abnormity 1", + "278": "Pv DCDC 1 DC Convetor EEPROM Abnormity 1 Second", + "279": "Pv DCDC 1 EDC Convetor EEPROM Abnormity 1", + "280": "Pv DCDC 1 DC Convertor General Overload", + "281": "Pv DCDC 1 DC Short Circuit", + "282": "Pv DCDC 1 Peak Pulse Current Protection", + "283": "Pv DCDC 1 DC Disconnect Abnormally On High Voltage Side Of DC Convetor", + "284": "Pv DCDC 1 Effective Pulse Value Overhigh", + "285": "Pv DCDC 1 DC Converte Severe Overload", + "286": "Pv DCDC 1 DC Breaker Disconnect Abnormally On High Voltage Side Of DC Convetor", + "287": "Pv DCDC 1 DC Breaker Disconnect Abnormally On Low Voltage Side Of DC Convetor", + "288": "Pv DCDC 1 DC Convetor Precharge Contactor Close Failed", + "289": "Pv DCDC 1 DC Convetor Main Contactor Close Failed", + "290": "Pv DCDC 1 AC Contactor State Abnormity Of DC Convetor", + "291": "Pv DCDC 1 DC Convetor Emergency Stop", + "292": "Pv DCDC 1 DC Converter Charging Gun Disconnected", + "293": "Pv DCDC 1 DC Current Abnormity Before DC Convetor Work", + "294": "Pv DCDC 1 Fu Se Disconnected", + "295": "Pv DCDC 1 DC Converter Hardware Current Or Voltage Fault", + "296": "Pv DCDC 1 DC Converter Crystal Oscillator Circuit Invalidation", + "297": "Pv DCDC 1 DC Converter Reset Circuit Invalidation", + "298": "Pv DCDC 1 DC Converter Sampling Circuit Invalidation", + "299": "Pv DCDC 1 DC Converter Digital IO Circuit Invalidation", + "300": "Pv DCDC 1 DC Converter PWM Circuit Invalidation", + "301": "Pv DCDC 1 DC Converter X 5045 Circuit Invalidation", + "302": "Pv DCDC 1 DC Converter CAN Circuit Invalidation", + "303": "Pv DCDC 1 DC Converter Software AND Hardware Protection Circuit Invalidation", + "304": "Pv DCDC 1 DC Converter Power Circuit Invalidation", + "305": "Pv DCDC 1 DC Converter CPU Invalidation", + "306": "Pv DCDC 1 DC Converter TINT 0 Interrupt Invalidation", + "307": "Pv DCDC 1 DC Converter ADC Interrupt Invalidation", + "308": "Pv DCDC 1 DC Converter CAPITN 4 Interrupt Invalidation", + "309": "Pv DCDC 1 DC Converter CAPINT 6 Interrupt Invalidation", + "310": "Pv DCDC 1 DC Converter T 3 PIN Tinterrupt Invalidation", + "311": "Pv DCDC 1 DC Converter T 4 PIN Tinterrupt Invalidation", + "312": "Pv DCDC 1 DC Converter PDPINTA Interrupt Invalidation", + "313": "Pv DCDC 1 DC Converter T 1 PINT Interrupt Invalidation Second", + "314": "Pv DCDC 1 DC Converter RESV Interrupt Invalidation", + "315": "Pv DCDC 1 DC Converter 100us Task Invalidation", + "316": "Pv DCDC 1 DC Converter Clock Invalidation", + "317": "Pv DCDC 1 DC Converter EMS Memory Invalidation", + "318": "Pv DCDC 1 DC Converter Exterior Communication Invalidation", + "319": "Pv DCDC 1 DC Converter IO Interface Invalidation", + "320": "Pv DCDC 1 DC Converter Input Voltage Bound Fault", + "321": "Pv DCDC 1 DC Converter Outter Voltage Bound Fault", + "322": "Pv DCDC 1 DC Converter Output Voltage Bound Fault", + "323": "Pv DCDC 1 DC Converter Induct Current Bound Fault", + "324": "Pv DCDC 1 DC Converter Input Current Bound Fault", + "325": "Pv DCDC 1 DC Converter Output Current Bound Fault", + "326": "Pv DCDC 1 DC Reactor Over Temperature", + "327": "Pv DCDC 1 DCIGBT Over Temperature", + "328": "Pv DCDC 1 DC Converter Chanel 3 Over Temperature", + "329": "Pv DCDC 1 DC Converter Chanel 4 Over Temperature", + "330": "Pv DCDC 1 DC Converter Chanel 5 Over Temperature", + "331": "Pv DCDC 1 DC Converter Chanel 6 Over Temperature", + "332": "Pv DCDC 1 DC Converter Chanel 7 Over Temperature", + "333": "Pv DCDC 1 DC Converter Chanel 8 Over Temperature", + "334": "Pv DCDC 1 DC Reactor Temperature Sampling Invalidation", + "335": "Pv DCDC 1 DCIGBT Temperature Sampling Invalidation", + "336": "Pv DCDC 1 DC Converter Chanel 3 Temperature Sampling Invalidation", + "337": "Pv DCDC 1 DC Converter Chanel 4 Temperature Sampling Invalidation", + "338": "Pv DCDC 1 DC Converter Chanel 5 Temperature Sampling Invalidation", + "339": "Pv DCDC 1 DC Converter Chanel 6 Temperature Sampling Invalidation", + "340": "Pv DCDC 1 DC Converter Chanel 7 Temperature Sampling Invalidation", + "341": "Pv DCDC 1 DC Converter Chanel 8 Temperature Sampling Invalidation", + "342": "Pv DCDC 1 DC Converter Inductance Current Sampling Invalidation", + "343": "Pv DCDC 1 Current Sampling Invalidation On The Low Voltage Side Of DC Converter", + "344": "Pv DCDC 1 Voltage Sampling Invalidation On The Low Voltage Side Of DC Converter", + "345": "Pv DCDC 1 Insulation Inspection Fault", + "346": "Pv DCDC 1 Neg Contactor Close Unsuccessly", + "347": "Pv DCDC 1 Neg Contactor Cut When Running" + } + }, + "io.openems.impl.device.pro.FeneconProEss": { + "warnings": { + "0": "Fail The System Should Be Stopped", + "1": "Common Low Voltage Alarm", + "2": "Common High Voltage Alarm", + "3": "Charging Over Current Alarm", + "4": "Discharging Over Current Alarm", + "5": "Over Temperature Alarm", + "6": "Interal Communication Abnormal", + "7": "Grid Undervoltage L 1", + "8": "Grid Overvoltage L 1", + "9": "Grid Under Frequency L 1", + "10": "Grid Over Frequency L 1", + "11": "Grid Power Supply Off L 1", + "12": "Grid Condition Unmeet L 1", + "13": "DC Under Voltage L 1", + "14": "Input Over Resistance L 1", + "15": "Combination Error L 1", + "16": "Comm With Inverter Error L 1", + "17": "Tme Error L 1", + "18": "Grid Undervoltage L 2", + "19": "Grid Overvoltage L 2", + "20": "Grid Under Frequency L 2", + "21": "Grid Over Frequency L 2", + "22": "Grid Power Supply Off L 2", + "23": "Grid Condition Unmeet L 2", + "24": "DC Under Voltage L 2", + "25": "Input Over Resistance L 2", + "26": "Combination Error L 2", + "27": "Comm With Inverter Error L 2", + "28": "Tme Error L 2", + "29": "Grid Undervoltage L 3", + "30": "Grid Overvoltage L 3", + "31": "Grid Under Frequency L 3", + "32": "Grid Over Frequency L 3", + "33": "Grid Power Supply Off L 3", + "34": "Grid Condition Unmeet L 3", + "35": "DC Under Voltage L 3", + "36": "Input Over Resistance L 3", + "37": "Combination Error L 3", + "38": "Comm With Inverter Error L 3", + "39": "Tme Error L 3", + "40": "OFF Grid" + }, + "faults": { + "0": "Control Current Overload 100 Percent L 1", + "1": "Control Current Overload 110 Percent L 1", + "2": "Control Current Overload 150 Percent L 1", + "3": "Control Current Overload 200 Percent L 1", + "4": "Control Current Overload 120 Percent L 1", + "5": "Control Current Overload 300 Percent L 1", + "6": "Control Transient Load 300 Percent L 1", + "7": "Grid Over Current L 1", + "8": "Locking Waveform Too Many Times L 1", + "9": "Inverter Voltage Zero Drift Error L 1", + "10": "Grid Voltage Zero Drift Error L 1", + "11": "Control Current Zero Drift Error L 1", + "12": "Inverter Current Zero Drift Error L 1", + "13": "Grid Current Zero Drift Error L 1", + "14": "PDP Protection L 1", + "15": "Hardware Control Current Protection L 1", + "16": "Hardware AC Voltage Protection L 1", + "17": "Hardware DC Current Protection L 1", + "18": "Hardware Temperature Protection L 1", + "19": "No Capturing Signal L 1", + "20": "DC Overvoltage L 1", + "21": "DC Disconnected L 1", + "22": "Inverter Undervoltage L 1", + "23": "Inverter Overvoltage L 1", + "24": "Current Sensor Fail L 1", + "25": "Voltage Sensor Fail L 1", + "26": "Power Uncontrollable L 1", + "27": "Current Uncontrollable L 1", + "28": "Fan Error L 1", + "29": "Phase Lack L 1", + "30": "Inverter Relay Fault L 1", + "31": "Grid Realy Fault L 1", + "32": "Control Panel Overtemp L 1", + "33": "Power Panel Overtemp L 1", + "34": "DC Input Overcurrent L 1", + "35": "Capacitor Overtemp L 1", + "36": "Radiator Overtemp L 1", + "37": "Transformer Overtemp L 1", + "38": "Combination Comm Error L 1", + "39": "EEPROM Error L 1", + "40": "Load Current Zero Drift Error L 1", + "41": "Current Limit R Error L 1", + "42": "Phase Sync Error L 1", + "43": "External PV Current Zero Drift Error L 1", + "44": "External Grid Current Zero Drift Error L 1", + "45": "Control Current Overload 100 Percent L 2", + "46": "Control Current Overload 110 Percent L 2", + "47": "Control Current Overload 150 Percent L 2", + "48": "Control Current Overload 200 Percent L 2", + "49": "Control Current Overload 120 Percent L 2", + "50": "Control Current Overload 300 Percent L 2", + "51": "Control Transient Load 300 Percent L 2", + "52": "Grid Over Current L 2", + "53": "Locking Waveform Too Many Times L 2", + "54": "Inverter Voltage Zero Drift Error L 2", + "55": "Grid Voltage Zero Drift Error L 2", + "56": "Control Current Zero Drift Error L 2", + "57": "Inverter Current Zero Drift Error L 2", + "58": "Grid Current Zero Drift Error L 2", + "59": "PDP Protection L 2", + "60": "Hardware Control Current Protection L 2", + "61": "Hardware AC Voltage Protection L 2", + "62": "Hardware DC Current Protection L 2", + "63": "Hardware Temperature Protection L 2", + "64": "No Capturing Signal L 2", + "65": "DC Overvoltage L 2", + "66": "DC Disconnected L 2", + "67": "Inverter Undervoltage L 2", + "68": "Inverter Overvoltage L 2", + "69": "Current Sensor Fail L 2", + "70": "Voltage Sensor Fail L 2", + "71": "Power Uncontrollable L 2", + "72": "Current Uncontrollable L 2", + "73": "Fan Error L 2", + "74": "Phase Lack L 2", + "75": "Inverter Relay Fault L 2", + "76": "Grid Realy Fault L 2", + "77": "Control Panel Overtemp L 2", + "78": "Power Panel Overtemp L 2", + "79": "DC Input Overcurrent L 2", + "80": "Capacitor Overtemp L 2", + "81": "Radiator Overtemp L 2", + "82": "Transformer Overtemp L 2", + "83": "Combination Comm Error L 2", + "84": "EEPROM Error L 2", + "85": "Load Current Zero Drift Error L 2", + "86": "Current Limit R Error L 2", + "87": "Phase Sync Error L 2", + "88": "External PV Current Zero Drift Error L 2", + "89": "External Grid Current Zero Drift Error L 2", + "90": "Control Current Overload 100 Percent L 3", + "91": "Control Current Overload 110 Percent L 3", + "92": "Control Current Overload 150 Percent L 3", + "93": "Control Current Overload 200 Percent L 3", + "94": "Control Current Overload 120 Percent L 3", + "95": "Control Current Overload 300 Percent L 3", + "96": "Control Transient Load 300 Percent L 3", + "97": "Grid Over Current L 3", + "98": "Locking Waveform Too Many Times L 3", + "99": "Inverter Voltage Zero Drift Error L 3", + "100": "Grid Voltage Zero Drift Error L 3", + "101": "Control Current Zero Drift Error L 3", + "102": "Inverter Current Zero Drift Error L 3", + "103": "Grid Current Zero Drift Error L 3", + "104": "PDP Protection L 3", + "105": "Hardware Control Current Protection L 3", + "106": "Hardware AC Voltage Protection L 3", + "107": "Hardware DC Current Protection L 3", + "108": "Hardware Temperature Protection L 3", + "109": "No Capturing Signal L 3", + "110": "DC Overvoltage L 3", + "111": "DC Disconnected L 3", + "112": "Inverter Undervoltage L 3", + "113": "Inverter Overvoltage L 3", + "114": "Current Sensor Fail L 3", + "115": "Voltage Sensor Fail L 3", + "116": "Power Uncontrollable L 3", + "117": "Current Uncontrollable L 3", + "118": "Fan Error L 3", + "119": "Phase Lack L 3", + "120": "Inverter Relay Fault L 3", + "121": "Grid Realy Fault L 3", + "122": "Control Panel Overtemp L 3", + "123": "Power Panel Overtemp L 3", + "124": "DC Input Overcurrent L 3", + "125": "Capacitor Overtemp L 3", + "126": "Radiator Overtemp L 3", + "127": "Transformer Overtemp L 3", + "128": "Combination Comm Error L 3", + "129": "EEPROM Error L 3", + "130": "Load Current Zero Drift Error L 3", + "131": "Current Limit R Error L 3", + "132": "Phase Sync Error L 3", + "133": "External PV Current Zero Drift Error L 3", + "134": "External Grid Current Zero Drift Error L 3", + "135": "System Fault", + "136": "Battery Fault", + "137": "PCS Fault" + } + }, + "io.openems.impl.device.byd.Bem125ktla01Ess": { + "warnings": { + "0": "Warning State", + "1": "Protection State", + "2": "Derating State", + "3": "Charge Forbidden", + "4": "Discharge Forbidden", + "5": "Status Abnormal Of AC Surge Protector", + "6": "Close Of Control Switch", + "7": "Emergency Stop", + "8": "Status Abnormal Of Frog Detector", + "9": "Serious Leakage", + "10": "Normal Leakage", + "11": "Failure Of Temperature Sensor In Control Cabinet", + "12": "Failure Of Humidity Sensor In Control Cabinet", + "13": "Failure Of Storage Device", + "14": "Exceeding Of Humidity In Control Cabinet" + } + }, + "io.openems.impl.device.minireadonly.FeneconMiniEss": { + "faults": { + "0": "BECU 1 Discharge Severe Overcurrent", + "1": "BECU 1 Charge Severe Overcurrent", + "2": "BECU 1 General Undervoltage", + "3": "BECU 1 Severe Overvoltage", + "4": "BECU 1 General Overvoltage", + "5": "BECU 1 Severe Undervoltage", + "6": "BECU 1 Inside CAN Broken", + "7": "BECU 1 General Undervoltage High Current Discharge", + "8": "BECU 1 BMU Error", + "9": "BECU 1 Current Sampling Invalidation", + "10": "BECU 1 Battery Fail", + "11": "BECU 1 Temperature Sampling Broken", + "12": "BECU 1 Contactor 1 Test Back Is Abnormal Turn On Abnormity", + "13": "BECU 1 Contactor 1 Test Back Is Abnormal Turn Off Abnormity", + "14": "BECU 1 Contactor 2 Test Back Is Abnormal Turn On Abnormity", + "15": "BECU 1 Contactor 2 Test Back Is Abnormal Turn Off Abnormity", + "16": "BECU 1 Severe High Temperature Fault", + "17": "BECU 1 Hall Invalidation", + "18": "BECU 1 Contactor Invalidation", + "19": "BECU 1 Outside CAN Broken", + "20": "BECU 1 Cathode Contactor Broken", + "21": "BECU 2 Discharge Severe Overcurrent", + "22": "BECU 2 Charge Severe Overcurrent", + "23": "BECU 2 General Undervoltage", + "24": "BECU 2 Severe Overvoltage", + "25": "BECU 2 General Overvoltage", + "26": "BECU 2 Severe Undervoltage", + "27": "BECU 2 Inside CAN Broken", + "28": "BECU 2 General Undervoltage High Current Discharge", + "29": "BECU 2 BMU Error", + "30": "BECU 2 Current Sampling Invalidation", + "31": "BECU 2 Battery Fail", + "32": "BECU 2 Temperature Sampling Broken", + "33": "BECU 2 Contactor 1 Test Back Is Abnormal Turn On Abnormity", + "34": "BECU 2 Contactor 1 Test Back Is Abnormal Turn Off Abnormity", + "35": "BECU 2 Contactor 2 Test Back Is Abnormal Turn On Abnormity", + "36": "BECU 2 Contactor 2 Test Back Is Abnormal Turn Off Abnormity", + "37": "BECU 2 Severe High Temperature Fault", + "38": "BECU 2 Hall Invalidation", + "39": "BECU 2 Contactor Invalidation", + "40": "BECU 2 Outside CAN Broken", + "41": "BECU 2 Cathode Contactor Broken", + "42": "No Available Battery Group", + "43": "Stack General Leakage", + "44": "Stack Severe Leakage", + "45": "Stack Starting Fail", + "46": "Stack Stopping Fail", + "47": "Battery Protection", + "48": "Stack And Group 1 CAN Communication Interrupt", + "49": "Stack And Group 2 CAN Communication Interrupt" + }, + "warnings": { + "0": "BECU 1 General Charge Over Current Alarm", + "1": "BECU 1 General Discharge Over Current Alarm", + "2": "BECU 1 Charge Current Limit Alarm", + "3": "BECU 1 Discharge Current Limit Alarm", + "4": "BECU 1 General High Voltage Alarm", + "5": "BECU 1 General Low Voltage Alarm", + "6": "BECU 1 Abnormal Voltage Change Alarm", + "7": "BECU 1 General High Temperature Alarm", + "8": "BECU 1 General Low Temperature Alarm", + "9": "BECU 1 Abnormal Temperature Change Alarm", + "10": "BECU 1 Severe High Voltage Alarm", + "11": "BECU 1 Severe Low Voltage Alarm", + "12": "BECU 1 Severe Low Temperature Alarm", + "13": "BECU 1 Severve Charge Over Current Alarm", + "14": "BECU 1 Severve Discharge Over Current Alarm", + "15": "BECU 1 Abnormal Cell Capacity Alarm", + "16": "BECU 1 Balanced Sampling Alarm", + "17": "BECU 1 Balanced Control Alarm", + "18": "BECU 1 Hall Sensor Does Not Work Accurately", + "19": "BECU 1 Generalleakage", + "20": "BECU 1 Severeleakage", + "21": "BECU 1 Contactor 1 Turn On Abnormity", + "22": "BECU 1 Contactor 1 Turn Off Abnormity", + "23": "BECU 1 Contactor 2 Turn On Abnormity", + "24": "BECU 1 Contactor 2 Turn Off Abnormity", + "25": "BECU 1 Contactor 4 Check Abnormity", + "26": "BECU 1 Contactor Current Unsafe", + "27": "BECU 1 Contactor 5 Check Abnormity", + "28": "BECU 1 High Voltage Offset", + "29": "BECU 1 Low Voltage Offset", + "30": "BECU 1 High Temperature Offset", + "31": "BECU 2 General Charge Over Current Alarm", + "32": "BECU 2 General Discharge Over Current Alarm", + "33": "BECU 2 Charge Current Limit Alarm", + "34": "BECU 2 Discharge Current Limit Alarm", + "35": "BECU 2 General High Voltage Alarm", + "36": "BECU 2 General Low Voltage Alarm", + "37": "BECU 2 Abnormal Voltage Change Alarm", + "38": "BECU 2 General High Temperature Alarm", + "39": "BECU 2 General Low Temperature Alarm", + "40": "BECU 2 Abnormal Temperature Change Alarm", + "41": "BECU 2 Severe High Voltage Alarm", + "42": "BECU 2 Severe Low Voltage Alarm", + "43": "BECU 2 Severe Low Temperature Alarm", + "44": "BECU 2 Severve Charge Over Current Alarm", + "45": "BECU 2 Severve Discharge Over Current Alarm", + "46": "BECU 2 Abnormal Cell Capacity Alarm", + "47": "BECU 2 Balanced Sampling Alarm", + "48": "BECU 2 Balanced Control Alarm", + "49": "BECU 2 Hall Sensor Does Not Work Accurately", + "50": "BECU 2 Generalleakage", + "51": "BECU 2 Severeleakage", + "52": "BECU 2 Contactor 1 Turn On Abnormity", + "53": "BECU 2 Contactor 1 Turn Off Abnormity", + "54": "BECU 2 Contactor 2 Turn On Abnormity", + "55": "BECU 2 Contactor 2 Turn Off Abnormity", + "56": "BECU 2 Contactor 4 Check Abnormity", + "57": "BECU 2 Contactor Current Unsafe", + "58": "BECU 2 Contactor 5 Check Abnormity", + "59": "BECU 2 High Voltage Offset", + "60": "BECU 2 Low Voltage Offset", + "61": "BECU 2 High Temperature Offset", + "62": "General Overcurrent Alarm At Cell Stack Charge", + "63": "General Overcurrent Alarm At Cell Stack Discharge", + "64": "Current Limit Alarm At Cell Stack Charge", + "65": "Current Limit Alarm At Cell Stack Discharge", + "66": "General Cell Stack High Voltage Alarm", + "67": "General Cell Stack Low Voltage Alarm", + "68": "Abnormal Cell Stack Voltage Change Alarm", + "69": "General Cell Stack High Temperature Alarm", + "70": "General Cell Stack Low Temperature Alarm", + "71": "Abnormal Cell Stack Temperature Change Alarm", + "72": "Severe Cell Stack High Voltage Alarm", + "73": "Severe Cell Stack Low Voltage Alarm", + "74": "Severe Cell Stack Low Temperature Alarm", + "75": "Severve Over Current Alarm At Cell Stack Dharge", + "76": "Severve Over Current Alarm At Cell Stack Discharge", + "77": "Abnormal Cell Stack Capacity Alarm", + "78": "The Parameter Of EEPROM In Cell Stack Lose Effectiveness", + "79": "Isolating Switch In Confluence Ark Break", + "80": "The Communication Between Cell Stack And Temperature Of Collector Break", + "81": "The Temperature Of Collector Fail", + "82": "Hall Sensor Do Not Work Accurately", + "83": "The Communication Of PCS Break", + "84": "Advanced Charging Or Main Contactor Close Abnormally", + "85": "Abnormal Sampled Voltage", + "86": "Abnormal Advanced Contactor Or Abnormal RS 485 Gallery Of PCS", + "87": "Abnormal Main Contactor", + "88": "General Cell Stack Leakage", + "89": "Severe Cell Stack Leakage", + "90": "Smoke Alarm", + "91": "The Communication Wire To Ammeter Break", + "92": "The Communication Wire To Dred Break" + } + }, + "io.openems.impl.device.pro.FeneconProPvMeter": { + "warnings": { + "0": "Negative Power L 1", + "1": "Negative Power L 2", + "2": "Negative Power L 3" + } + }, + "io.openems.impl.device.refu.RefuEss": { + "faults": { + "0": "BMS In Error", + "1": "BMS In Error Second", + "2": "BMS Undervoltage", + "3": "BMS Overcurrent", + "4": "Error BMS Limits Not Initialized", + "5": "Connect Error", + "6": "Overvoltage Warning", + "7": "Undervoltage Warning", + "8": "Overcurrent Warning", + "9": "BMS Ready", + "10": "TREX Ready", + "11": "No Enable Batery Group Or Usable Battery Group", + "12": "Normal Leakage Of Battery Group", + "13": "Serious Leakage Of Battery Group", + "14": "Battery Start Failure", + "15": "Battery Stop Failure", + "16": "Interruption Of CAN Communication", + "17": "Interruption Of CAN Communication Between Battery Group And Controller", + "18": "Emergency Stop Abnormal Of Auxiliary Collector", + "19": "Leakage Self Detection On Negative", + "20": "Leakage Self Detection On Positive", + "21": "Self Detection Failure On Battery", + "22": "CAN Communication Interruption Between Battery Group And Group 1", + "23": "CAN Communication Interruption Between Battery Group And Group 2", + "24": "CAN Communication Interruption Between Battery Group And Group 3", + "25": "CAN Communication Interruption Between Battery Group And Group 4", + "26": "Main Contractor Abnormal In Battery Self Detect Group 1", + "27": "Main Contractor Abnormal In Battery Self Detect Group 2", + "28": "Main Contractor Abnormal In Battery Self Detect Group 3", + "29": "Main Contractor Abnormal In Battery Self Detect Group 4", + "30": "Pre Charge Contractor Abnormal On Battery Self Detect Group 1", + "31": "Pre Charge Contractor Abnormal On Battery Self Detect Group 2", + "32": "Pre Charge Contractor Abnormal On Battery Self Detect Group 3", + "33": "Pre Charge Contractor Abnormal On Battery Self Detect Group 4", + "34": "Main Contact Failure On Battery Control Group 1", + "35": "Main Contact Failure On Battery Control Group 2", + "36": "Main Contact Failure On Battery Control Group 3", + "37": "Main Contact Failure On Battery Control Group 4", + "38": "Pre Charge Failure On Battery Control Group 1", + "39": "Pre Charge Failure On Battery Control Group 2", + "40": "Pre Charge Failure On Battery Control Group 3", + "41": "Pre Charge Failure On Battery Control Group 4", + "42": "Sampling Circuit Abnormal For BMU", + "43": "Power Cable Disconnect Failure", + "44": "Sampling Circuit Disconnect Failure", + "45": "CAN Disconnect For Master And Slave", + "46": "Sammpling Circuit Failure", + "47": "Single Battery Failure", + "48": "Circuit Detection Abnormal For Main Contactor", + "49": "Circuit Detection Abnormal For Main Contactor Second", + "50": "Circuit Detection Abnormal For Fancontactor", + "51": "BMU Power Contactor Circuit Detection Abnormal", + "52": "Central Contactor Circuit Detection Abnormal", + "53": "Serious Temperature Fault", + "54": "Communication Fault For System Controller", + "55": "Frog Alarm", + "56": "Fuse Fault", + "57": "Normal Leakage", + "58": "Serious Leakage", + "59": "CAN Disconnection Between Battery Group And Battery Stack", + "60": "Central Contactor Circuit Open", + "61": "BMU Power Contactor Open" + }, + "warnings": { + "0": "Normal Charging Over Current", + "1": "Charginig Current Over Limit", + "2": "Discharging Current Over Limit", + "3": "Normal High Voltage", + "4": "Normal Low Voltage", + "5": "Abnormal Voltage Variation", + "6": "Normal High Temperature", + "7": "Normal Low Temperature", + "8": "Abnormal Temperature Variation", + "9": "Serious High Voltage", + "10": "Serious Low Voltage", + "11": "Serious Low Temperature", + "12": "Charging Serious Over Current", + "13": "Discharging Serious Over Current", + "14": "Abnormal Capacity Alarm", + "15": "EEPROM Parameter Failure", + "16": "Switch Of Inside Combined Cabinet", + "17": "Should Not Be Connected To Grid Due To The DC Side Condition", + "18": "Emergency Stop Require From System Controller", + "19": "Battery Group 1 Enable And Not Connected To Grid", + "20": "Battery Group 2 Enable And Not Connected To Grid", + "21": "Battery Group 3 Enable And Not Connected To Grid", + "22": "Battery Group 4 Enable And Not Connected To Grid", + "23": "The Isolation Switch Of Battery Group 1 Open", + "24": "The Isolation Switch Of Battery Group 2 Open", + "25": "The Isolation Switch Of Battery Group 3 Open", + "26": "The Isolation Switch Of Battery Group 4 Open", + "27": "Balancing Sampling Failure Of Battery Group 1", + "28": "Balancing Sampling Failure Of Battery Group 2", + "29": "Balancing Sampling Failure Of Battery Group 3", + "30": "Balancing Sampling Failure Of Battery Group 4", + "31": "Balancing Control Failure Of Battery Group 1", + "32": "Balancing Control Failure Of Battery Group 2", + "33": "Balancing Control Failure Of Battery Group 3", + "34": "Balancing Control Failure Of Battery Group 4" + } + }, + "io.openems.impl.device.mini.FeneconMiniEss": { + "faults": { + "0": "Control Current Overload 100 Percent", + "1": "Control Current Overload 110 Percent", + "2": "Control Current Overload 150 Percent", + "3": "Control Current Overload 200 Percent", + "4": "Control Current Overload 120 Percent", + "5": "Control Current Overload 300 Percent", + "6": "Control Transient Load 300 Percent", + "7": "Grid Over Current", + "8": "Locking Waveform Too Many Times", + "9": "Inverter Voltage Zero Drift Error", + "10": "Grid Voltage Zero Drift Error", + "11": "Control Current Zero Drift Error", + "12": "Inverter Current Zero Drift Error", + "13": "Grid Current Zero Drift Error", + "14": "PDP Protection", + "15": "Hardware Control Current Protection", + "16": "Hardware AC Volt Protection", + "17": "Hardware DC Current Protection", + "18": "Hardware Temperature Protection", + "19": "No Capturing Signal", + "20": "DC Overvoltage", + "21": "DC Disconnected", + "22": "Inverter Undervoltage", + "23": "Inverter Overvoltage", + "24": "Current Sensor Fail", + "25": "Voltage Sensor Fail", + "26": "Power Uncontrollable", + "27": "Current Uncontrollable", + "28": "Fan Error", + "29": "Phase Lack", + "30": "Inverter Relay Fault", + "31": "Grid Relay Fault", + "32": "Control Panel Overtemp", + "33": "Power Panel Overtemp", + "34": "DC Input Overcurrent", + "35": "Capacitor Overtemp", + "36": "Radiator Overtemp", + "37": "Transformer Overtemp", + "38": "Combination Comm Error", + "39": "EEPROM Error", + "40": "Load Current Zero Drift Error", + "41": "Current Limit R Error", + "42": "Phase Sync Error", + "43": "External PV Current Zero Drift Error", + "44": "External Grid Current Zero Drift Error" + }, + "warnings": { + "0": "Fail The System Should Be Stopped", + "1": "Common Low Voltage Alarm", + "2": "Common High Voltage Alarm", + "3": "Charging Over Current Alarm", + "4": "Discharging Over Current Alarm", + "5": "Over Temperature Alarm", + "6": "Interal Communication Abnormal", + "7": "Grid Undervoltage", + "8": "Grid Overvoltage", + "9": "Grid Under Frequency", + "10": "Grid Over Frequency", + "11": "Grid Power Supply Off", + "12": "Grid Condition Unmeet", + "13": "DC Under Voltage", + "14": "Input Over Resistance", + "15": "Combination Error", + "16": "Comm With Inverter Error", + "17": "Tme Error" + } + }, + "io.openems.impl.protocol.modbus.ModbusRtu": { + "faults": { + "0": "Configuration Fault", + "1": "Connection Fault" + } + }, + "io.openems.impl.protocol.modbus.ModbusTcp": { + "faults": { + "0": "Configuration Fault", + "1": "Connection Fault" + } + } +}; \ No newline at end of file From d674c0f3395b725585f7ada41064fb16ea60a304 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:13:00 +0100 Subject: [PATCH 133/156] Generalize the way subscribes to currentData are handled by overview component --- .../app/device/overview/overview.component.ts | 72 ++++++++++++++----- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/ui/src/app/device/overview/overview.component.ts b/ui/src/app/device/overview/overview.component.ts index c964b8ee49d..81a61776694 100644 --- a/ui/src/app/device/overview/overview.component.ts +++ b/ui/src/app/device/overview/overview.component.ts @@ -51,22 +51,7 @@ export class OverviewComponent implements OnInit, OnDestroy { // get widgets this.widgets = config.getWidgets(); - // subscribe channels - let channels = config.getImportantChannels(); - device.subscribeCurrentData(channels) - .takeUntil(this.stopOnDestroy) - .subscribe(currentData => { - this.currentData = currentData; - - // resubscribe on timeout - clearInterval(this.currentDataTimeout); - this.currentDataTimeout = window.setInterval(() => { - this.currentData = null; - if (this.websocket.status == 'online') { - device.subscribeCurrentData(channels); - } - }, Websocket.TIMEOUT); - }); + this.subscribe(); } // TODO fieldstatus // /* @@ -95,4 +80,59 @@ export class OverviewComponent implements OnInit, OnDestroy { this.stopOnDestroy.next(); this.stopOnDestroy.complete(); } + + private requiredSubscribes: { [componentId: string]: DefaultTypes.ChannelAddresses } = {}; + + onRequiredSubscribes(componentId: string, subscribes: DefaultTypes.ChannelAddresses) { + this.requiredSubscribes[componentId] = subscribes; + this.subscribe(); + } + + private subscription: Subscription = null; + + private subscribe() { + // abort if device or config are missing + if (this.device == null || this.config == null) { + if (this.subscription != null) { + this.subscription.unsubscribe(); + } + return; + } + // merge channels from requiredSubscribes + //let channels: DefaultTypes.ChannelAddresses = {} // TODO move getImportantChannels to sub-components + event + let channels: DefaultTypes.ChannelAddresses = this.config.getImportantChannels(); + for (let componentId in this.requiredSubscribes) { + let requiredSubscribe = this.requiredSubscribes[componentId]; + for (let thingId in requiredSubscribe) { + if (thingId in channels) { + for (let channelId of requiredSubscribe[thingId]) { + if (!channels[thingId].includes(channelId)) { + channels[thingId].push(channelId); + } + } + } else { + channels[thingId] = requiredSubscribe[thingId]; + } + } + } + + if (this.subscription != null) { + this.subscription.unsubscribe(); + } + this.subscription = this.device.subscribeCurrentData(channels) + .takeUntil(this.stopOnDestroy) + .subscribe(currentData => { + this.currentData = currentData; + + // resubscribe on timeout + clearInterval(this.currentDataTimeout); + this.currentDataTimeout = window.setInterval(() => { + this.currentData = null; + if (this.websocket.status == 'online') { + // TODO + console.warn('timeout...') + } + }, Websocket.TIMEOUT); + }); + } } \ No newline at end of file From 65f5473cb49ba33058573f07f8d5f0898dbd3e51 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:13:57 +0100 Subject: [PATCH 134/156] Add "State" widget to show current warnings and faults --- .../device/overview/overview.component.html | 2 +- .../overview/state/state.component.html | 83 ++++++---- .../device/overview/state/state.component.ts | 143 +++++++++++++++--- 3 files changed, 178 insertions(+), 50 deletions(-) diff --git a/ui/src/app/device/overview/overview.component.html b/ui/src/app/device/overview/overview.component.html index ae6734654e7..0d0be923489 100644 --- a/ui/src/app/device/overview/overview.component.html +++ b/ui/src/app/device/overview/overview.component.html @@ -6,7 +6,7 @@ - +
        diff --git a/ui/src/app/device/overview/state/state.component.html b/ui/src/app/device/overview/state/state.component.html index 9a10dd1e714..3869f529009 100644 --- a/ui/src/app/device/overview/state/state.component.html +++ b/ui/src/app/device/overview/state/state.component.html @@ -1,35 +1,54 @@ - - -
        - - - error - Warnungen und Fehler - - + +
        + + + error + Aktuelle Warnungen und Fehler + - - - - - - - - - - -
        {{ thingId }} - Warnung - Fehler -
        -
        + + + + + + + + +
        + {{ warningsAndFaults.thing.name }} +
        + ({{ warningsAndFaults.thing.id }}) +
        + {{ warningsAndFaults.thing.id }} +
        + + + + + + +
        + Fehler: + {{ line.name }} +
        + + + + + + +
        + Warnung: + {{ line.name }} +
        +
        +
        - - - -
        -
        -
        + + + +
        +
        \ No newline at end of file diff --git a/ui/src/app/device/overview/state/state.component.ts b/ui/src/app/device/overview/state/state.component.ts index c5eba999468..b1cede0bedf 100644 --- a/ui/src/app/device/overview/state/state.component.ts +++ b/ui/src/app/device/overview/state/state.component.ts @@ -1,10 +1,26 @@ -import { Component, Input, OnDestroy } from '@angular/core'; +import { Component, Input, OnDestroy, EventEmitter, Output } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Device } from '../../../shared/device/device'; import { Utils } from '../../../shared/service/utils'; import { DefaultTypes } from '../../../shared/service/defaulttypes'; import { CurrentDataAndSummary } from '../../../shared/device/currentdata'; +import { THING_STATES } from './thingstates'; +import { ConfigImpl } from '../../../shared/device/config'; + +interface WarningOrFault { + channelId: string, + name: string +} + +interface WarningsAndFaults { + thing: { + id: string, + name?: string + }, + warnings: WarningOrFault[], + faults: WarningOrFault[] +} @Component({ selector: 'state', @@ -13,29 +29,122 @@ import { CurrentDataAndSummary } from '../../../shared/device/currentdata'; export class StateComponent { @Input() - public device: Device; - - public _currentData: CurrentDataAndSummary = null; - private lastThingIds: string[] + public config: ConfigImpl; @Input() set currentData(currentData: CurrentDataAndSummary) { - this._currentData = currentData; - let thingIds = []; - for (let thingId of Object.keys(currentData.data)) { - let thing = currentData.data[thingId]; - if (thing['State'] != 0) { - thingIds.push(thingId); + this.generateRequiredSubscribes(currentData); + this.fillWarningsAndFaultsList(currentData); + } + + /** + * Generates the requiredSubscribes. + * + * If a Things has 'State' different to 0, it has a warning or fault. In that case define the needed subscribe + * channels (like 'ess0/Fault/0') and emit the 'requiredSubscribes' event. + * + * @param currentData + */ + private generateRequiredSubscribes(currentData: CurrentDataAndSummary) { + let subscribesChanged: boolean = false; + let newRequiredSubscribes: DefaultTypes.ChannelAddresses = {}; + if (currentData == null && this.lastRequiredSubscribes != {}) { + subscribesChanged = true; + } else { + for (let thingId of Object.keys(currentData.data)) { + let thing = currentData.data[thingId]; + if (thing['State'] != 0) { + // Thing has a warning or fault + if (thingId in this.lastRequiredSubscribes) { + // was like this before -> copy subscribes from last time + newRequiredSubscribes[thingId] = this.lastRequiredSubscribes[thingId]; + } else { + // this is new -> generate required subscribes + // TODO + newRequiredSubscribes[thingId] = ["Fault/0", "Fault/1", "Warning/0"]; + subscribesChanged = true; + } + } else { + // Thing has no warning or fault + if (thingId in this.lastRequiredSubscribes) { + // it had an error before -> do not add to required subscribes + subscribesChanged = true; + } + } } } - console.log(thingIds); - this.device.warningOrFaultQuery(thingIds).then(warningsOrFaults => { - console.log(warningsOrFaults); - }); + if (subscribesChanged) { + this.requiredSubscribes.emit(newRequiredSubscribes); + this.lastRequiredSubscribes = newRequiredSubscribes; + } } + private lastRequiredSubscribes: DefaultTypes.ChannelAddresses = {}; + @Output() + public requiredSubscribes = new EventEmitter(); - @Input() - public config: DefaultTypes.Config; + /** + * Generates the list of warnings and faults that is shown in the widget + * + * @param currentData + */ + private fillWarningsAndFaultsList(currentData: CurrentDataAndSummary) { + let warningsAndFaultss: WarningsAndFaults[] = []; + if (currentData != null) { + for (let thingId in currentData.data) { + let thing = currentData.data[thingId]; + if ('State' in thing && thing['State'] != 0) { + let warnings: WarningOrFault[] = []; + let faults: WarningOrFault[] = []; + for (let channelId of Object.keys(thing)) { + if (thing[channelId] != 0) { + if (channelId.startsWith('Fault/')) { + faults.push(this.getWarningOrFault(thingId, channelId, 'fault')); + } else if (channelId.startsWith('Warning/')) { + warnings.push(this.getWarningOrFault(thingId, channelId, 'warning')); + } + } + } + if (faults.length > 0 || warnings.length > 0) { + // get Thing name + let name = null; + if (this.config.storageThings.includes(thingId)) { + name = "Speichersystem" + } + warningsAndFaultss.push({ + thing: { + id: thingId, + name: name + }, + warnings: warnings, + faults: faults + }); + } + } + } + } + this.warningsAndFaultss = warningsAndFaultss; + }; + + public warningsAndFaultss: WarningsAndFaults[] = []; + + private getWarningOrFault(thingId: string, channelId: string, type: 'fault' | 'warning'): WarningOrFault { + let id = channelId.substr(type.length + 1); + let clazz = this.config.things[thingId].class; + if (clazz instanceof Array) { + clazz = clazz[0]; + } + let name = "Undefined " + type + " (" + channelId + ")"; + if (clazz in THING_STATES) { + let meta = THING_STATES[clazz]; + if (meta[type + 's'][id]) { + name = meta[type + 's'][id]; + } + } + return { + channelId: channelId, + name: name + }; + } constructor(public utils: Utils) { } } From 942ad8639f66827f26921b744139a73f8e574e24 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:17:18 +0100 Subject: [PATCH 135/156] Revert other approaches for State widget + clean --- .../impl/device/simulator/WarningEss.java | 20 ++++++++++++++ ...nems.backend.edgewebsocket.provider.bndrun | 15 ++++++++++- ui/src/app/shared/device/device.ts | 12 --------- .../haswarningorfault.pipe.ts | 27 ------------------- ui/src/app/shared/service/defaultmessages.ts | 13 --------- ui/src/app/shared/service/defaulttypes.ts | 7 ----- ui/src/app/shared/shared.module.ts | 3 --- 7 files changed, 34 insertions(+), 63 deletions(-) create mode 100644 edge/src/io/openems/impl/device/simulator/WarningEss.java delete mode 100644 ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts diff --git a/edge/src/io/openems/impl/device/simulator/WarningEss.java b/edge/src/io/openems/impl/device/simulator/WarningEss.java new file mode 100644 index 00000000000..bf9ccb448e7 --- /dev/null +++ b/edge/src/io/openems/impl/device/simulator/WarningEss.java @@ -0,0 +1,20 @@ +package io.openems.impl.device.simulator; + +import io.openems.api.channel.thingstate.WarningEnum; +import io.openems.common.types.ThingStateInfo; + +@ThingStateInfo(reference = SimulatorAsymmetricEss.class) +public enum WarningEss implements WarningEnum { + SimulatedWarning(0); + + private final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun b/io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun index 9a7da11fd64..7632c7c7c19 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun +++ b/io.openems.backend.edgewebsocket.impl.provider/io.openems.backend.edgewebsocket.provider.bndrun @@ -16,5 +16,18 @@ JPM-Command: provider org.apache.felix.scr;version='[2.0.2,2.0.3)',\ org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ - slf4j.api;version='[1.8.0,1.8.1)' + slf4j.api;version='[1.8.0,1.8.1)',\ + com.google.gson;version='[2.8.2,2.8.3)',\ + com.google.guava;version='[19.0.0,19.0.1)',\ + io.openems.backend.metadata.file.provider;version=snapshot,\ + io.openems.backend.timedata.influx.provider;version=snapshot,\ + io.openems.backend.uiwebsocket.impl.provider;version=snapshot,\ + io.openems.common;version=snapshot,\ + io.openems.wrapper.websocket;version=snapshot,\ + org.apache.servicemix.bundles.influxdb-java;version='[2.3.0,2.3.1)',\ + org.apache.servicemix.bundles.okhttp;version='[2.7.5,2.7.6)',\ + org.apache.servicemix.bundles.okio;version='[1.13.0,1.13.1)',\ + org.apache.servicemix.bundles.retrofit;version='[1.9.0,1.9.1)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.osgi.service.event;version='[1.3.1,1.3.2)' -runrequires: osgi.identity;filter:='(osgi.identity=io.openems.backend.edgewebsocket.impl.provider)' \ No newline at end of file diff --git a/ui/src/app/shared/device/device.ts b/ui/src/app/shared/device/device.ts index 14eca40f47a..eb517919dfc 100644 --- a/ui/src/app/shared/device/device.ts +++ b/ui/src/app/shared/device/device.ts @@ -145,18 +145,6 @@ export class Device { }) } - public warningOrFaultQuery(thingIds: string[]): Promise { - let replyStream = this.sendMessageWithReply(DefaultMessages.warningOrFaultQuery(this.edgeId, thingIds)); - // wait for reply - return new Promise((resolve, reject) => { - replyStream.first().subscribe(reply => { - let warningsOrFaults = (reply as DefaultMessages.HistoricDataReply).warningsOrFaults; - this.removeReplyStream(reply); - resolve(warningsOrFaults); - }); - }) - } - /** * Mark this device as online or offline * @param online diff --git a/ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts b/ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts deleted file mode 100644 index 96b0e4e9d65..00000000000 --- a/ui/src/app/shared/pipe/haswarningorfault/haswarningorfault.pipe.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; - -import { DefaultTypes } from '../../service/defaulttypes'; - -/** - * Runs a deep search in the given currentData object for a Channel 'State' which is non-equal to zero. - * Use like: *ngFor="let thingId in currentData | haswarningorfault" - */ -@Pipe({ - name: 'haswarningorfault' -}) -export class HasWarningOrFaultPipe implements PipeTransform { - transform(currentData: DefaultTypes.Data): DefaultTypes.Data { - if (currentData == null) { - return {} - } - let result = {} - for (let thingId of Object.keys(currentData.data)) { - let thing = currentData.data[thingId] - if (thing['State'] != 0) { - result[thingId] = thing - } - } - console.log(result) - return result - } -} \ No newline at end of file diff --git a/ui/src/app/shared/service/defaultmessages.ts b/ui/src/app/shared/service/defaultmessages.ts index 28cff6dd165..72319b0918d 100644 --- a/ui/src/app/shared/service/defaultmessages.ts +++ b/ui/src/app/shared/service/defaultmessages.ts @@ -85,19 +85,6 @@ export class DefaultMessages { } }; - public static warningOrFaultQuery(edgeId: number, thingIds: string[]): DefaultTypes.IdentifiedMessage { - return { - messageId: { - ui: UUID.UUID() - }, - edgeId: edgeId, - warningsOrFaults: { - mode: "query", - thingIds: thingIds - } - } - } - public static logSubscribe(edgeId: number): DefaultTypes.IdentifiedMessage { return { messageId: { diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index 8432e2188f4..9243928ec15 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -52,13 +52,6 @@ export module DefaultTypes { }] } - export interface WarningsOrFaults { - [thingId: string]: [{ - level: 'warning' | 'fault', - name: string - }] - } - export interface Summary { storage: { soc: number, diff --git a/ui/src/app/shared/shared.module.ts b/ui/src/app/shared/shared.module.ts index 852b3153089..4f925bed93e 100644 --- a/ui/src/app/shared/shared.module.ts +++ b/ui/src/app/shared/shared.module.ts @@ -40,7 +40,6 @@ import { SocChartComponent } from './../device/history/chart/socchart/socchart.c import { AbstractConfigComponent } from './config/abstractconfig.component'; import { ExistingThingComponent } from './config/existingthing.component'; import { ChannelComponent } from './config/channel.component'; -import { HasWarningOrFaultPipe } from './pipe/haswarningorfault/haswarningorfault.pipe'; @NgModule({ imports: [ @@ -64,7 +63,6 @@ import { HasWarningOrFaultPipe } from './pipe/haswarningorfault/haswarningorfaul SignPipe, IsclassPipe, HasclassPipe, - HasWarningOrFaultPipe, // components SocChartComponent, AbstractConfigComponent, @@ -78,7 +76,6 @@ import { HasWarningOrFaultPipe } from './pipe/haswarningorfault/haswarningorfaul ClassnamePipe, IsclassPipe, HasclassPipe, - HasWarningOrFaultPipe, // modules BrowserAnimationsModule, ChartsModule, From d474cba5baa5729b4b44b8ba51a9ea49fb9c56e6 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 7 Mar 2018 20:33:27 +0100 Subject: [PATCH 136/156] Fix line endings --- .../openems/impl/device/byd/WarningEss.java | 44 ++--- .../impl/device/commercial/FaultCharger.java | 176 +++++++++--------- .../impl/device/commercial/FaultEss.java | 70 +++---- .../device/commercial/WarningCharger.java | 114 ++++++------ .../impl/device/commercial/WarningEss.java | 132 ++++++------- .../io/openems/impl/device/mini/FaultEss.java | 56 +++--- .../openems/impl/device/mini/WarningEss.java | 42 ++--- .../impl/device/minireadonly/FaultEss.java | 54 +++--- .../impl/device/minireadonly/WarningEss.java | 76 ++++---- .../io/openems/impl/device/refu/FaultEss.java | 68 +++---- .../openems/impl/device/refu/WarningEss.java | 58 +++--- .../impl/device/simulator/SimulatorTools.java | 74 ++++---- 12 files changed, 482 insertions(+), 482 deletions(-) diff --git a/edge/src/io/openems/impl/device/byd/WarningEss.java b/edge/src/io/openems/impl/device/byd/WarningEss.java index ec153c243d7..9472ad08fd7 100644 --- a/edge/src/io/openems/impl/device/byd/WarningEss.java +++ b/edge/src/io/openems/impl/device/byd/WarningEss.java @@ -1,22 +1,22 @@ -package io.openems.impl.device.byd; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum { - - WarningState(0), ProtectionState(1), DeratingState(2), ChargeForbidden(3), DischargeForbidden(4), StatusAbnormalOfACSurgeProtector(5), - CloseOfControlSwitch(6), EmergencyStop(7), StatusAbnormalOfFrogDetector(8), SeriousLeakage(9), NormalLeakage(10), - FailureOfTemperatureSensorInControlCabinet(11), FailureOfHumiditySensorInControlCabinet(12), FailureOfStorageDevice(13), - ExceedingOfHumidityInControlCabinet(14); - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.byd; + +import io.openems.api.channel.thingstate.WarningEnum; + +public enum WarningEss implements WarningEnum { + + WarningState(0), ProtectionState(1), DeratingState(2), ChargeForbidden(3), DischargeForbidden(4), StatusAbnormalOfACSurgeProtector(5), + CloseOfControlSwitch(6), EmergencyStop(7), StatusAbnormalOfFrogDetector(8), SeriousLeakage(9), NormalLeakage(10), + FailureOfTemperatureSensorInControlCabinet(11), FailureOfHumiditySensorInControlCabinet(12), FailureOfStorageDevice(13), + ExceedingOfHumidityInControlCabinet(14); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/FaultCharger.java b/edge/src/io/openems/impl/device/commercial/FaultCharger.java index 34358945692..d5e2f2fe24e 100644 --- a/edge/src/io/openems/impl/device/commercial/FaultCharger.java +++ b/edge/src/io/openems/impl/device/commercial/FaultCharger.java @@ -1,88 +1,88 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultCharger implements FaultEnum{ - HighVoltageSideOfDCConverterUndervoltage(0), HighVoltageSideOfDCConverterOvervoltage(1), LowVoltageSideOfDCConverterUndervoltage(2), - LowVoltageSideOfDCConverterOvervoltage(3), HighVoltageSideOfDCConverterOvercurrentFault(4), LowVoltageSideOfDCConverterOvercurrentFault(5), DCConverterIGBTFault(6), DCConverterPrechargeUnmet(7), - BECUCommunicationDisconnected(8), DCConverterCommunicationDisconnected(9), CurrentConfigurationOverRange(10), TheBatteryRequestStop(11), OvercurrentRelayFault(12), LightningProtectionDeviceFault(13), - DCConverterPriamaryContactorDisconnectedAbnormally(14), DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(15), DCConvetorEEPROMAbnormity1(16), DCConvetorEEPROMAbnormity1Second(17), - EDCConvetorEEPROMAbnormity1(18), DCConvertorGeneralOverload(19), DCShortCircuit(20), PeakPulseCurrentProtection(21), DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(22), EffectivePulseValueOverhigh(23), - DCConverteSevereOverload(24), DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(25), DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(26), DCConvetorPrechargeContactorCloseFailed(27), - DCConvetorMainContactorCloseFailed(28), ACContactorStateAbnormityOfDCConvetor(29), DCConvetorEmergencyStop(30), DCConverterChargingGunDisconnected(31), DCCurrentAbnormityBeforeDCConvetorWork(32), FuSeDisconnected(33), - DCConverterHardwareCurrentOrVoltageFault(34), DCConverterCrystalOscillatorCircuitInvalidation(35), DCConverterResetCircuitInvalidation(36), DCConverterSamplingCircuitInvalidation(37), - DCConverterDigitalIOCircuitInvalidation(38), DCConverterPWMCircuitInvalidation(39), DCConverterX5045CircuitInvalidation(40), DCConverterCANCircuitInvalidation(41), DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(42), - DCConverterPowerCircuitInvalidation(43), DCConverterCPUInvalidation(44), DCConverterTINT0InterruptInvalidation(45), DCConverterADCInterruptInvalidation(46), DCConverterCAPITN4InterruptInvalidation(47), - DCConverterCAPINT6InterruptInvalidation(48), DCConverterT3PINTinterruptInvalidation(49), DCConverterT4PINTinterruptInvalidation(50), DCConverterPDPINTAInterruptInvalidation(51), DCConverterT1PINTInterruptInvalidation(52), - DCConverterRESVInterruptInvalidation(53), DCConverter100usTaskInvalidation(54), DCConverterClockInvalidation(55), DCConverterEMSMemoryInvalidation(56), DCConverterExteriorCommunicationInvalidation(57), - DCConverterIOInterfaceInvalidation(58), DCConverterInputVoltageBoundFault(59), DCConverterOutterVoltageBoundFault(60), DCConverterOutputVoltageBoundFault(61), DCConverterInductCurrentBoundFault(62), - DCConverterInputCurrentBoundFault(63), DCConverterOutputCurrentBoundFault(64), DCReactorOverTemperature(65), DCIGBTOverTemperature(66), DCConverterChanel3OverTemperature(67), DCConverterChanel4OverTemperature(68), - DCConverterChanel5OverTemperature(69), DCConverterChanel6OverTemperature(70), DCConverterChanel7OverTemperature(71), DCConverterChanel8OverTemperature(72), DCReactorTemperatureSamplingInvalidation(73), DCIGBTTemperatureSamplingInvalidation(74), - DCConverterChanel3TemperatureSamplingInvalidation(75), DCConverterChanel4TemperatureSamplingInvalidation(76), DCConverterChanel5TemperatureSamplingInvalidation(77), DCConverterChanel6TemperatureSamplingInvalidation(78), - DCConverterChanel7TemperatureSamplingInvalidation(79), DCConverterChanel8TemperatureSamplingInvalidation(80), DCConverterInductanceCurrentSamplingInvalidation(81), CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(82), - VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(83), InsulationInspectionFault(84), NegContactorCloseUnsuccessly(85), NegContactorCutWhenRunning(86), BmsDCDC1HighVoltageSideOfDCConverterUndervoltage(87), - BmsDCDC1HighVoltageSideOfDCConverterOvervoltage(88), BmsDCDC1LowVoltageSideOfDCConverterUndervoltage(89), BmsDCDC1LowVoltageSideOfDCConverterOvervoltage(90), BmsDCDC1HighVoltageSideOfDCConverterOvercurrentFault(91), - BmsDCDC1LowVoltageSideOfDCConverterOvercurrentFault(92), BmsDCDC1DCConverterIGBTFault(93), BmsDCDC1DCConverterPrechargeUnmet(94), BmsDCDC1BECUCommunicationDisconnected(95), BmsDCDC1DCConverterCommunicationDisconnected(96), - BmsDCDC1CurrentConfigurationOverRange(97), BmsDCDC1TheBatteryRequestStop(98), BmsDCDC1OvercurrentRelayFault(99), BmsDCDC1LightningProtectionDeviceFault(100), BmsDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(101), - BmsDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(102), BmsDCDC1DCConvetorEEPROMAbnormity1(103), BmsDCDC1DCConvetorEEPROMAbnormity1Second(104), BmsDCDC1EDCConvetorEEPROMAbnormity1(105), BsmDCDC1DCConvertorGeneralOverload(106), - BsmDCDC1DCShortCircuit(107), BsmDCDC1PeakPulseCurrentProtection(108), BsmDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(109), BsmDCDC1EffectivePulseValueOverhigh(110), BsmDCDC1DCConverteSevereOverload(111), - BsmDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(112), BsmDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(113), BsmDCDC1DCConvetorPrechargeContactorCloseFailed(114), BsmDCDC1DCConvetorMainContactorCloseFailed(115), - BsmDCDC1ACContactorStateAbnormityOfDCConvetor(116), BsmDCDC1DCConvetorEmergencyStop(117), BsmDCDC1DCConverterChargingGunDisconnected(118), BsmDCDC1DCCurrentAbnormityBeforeDCConvetorWork(119), BsmDCDC1FuSeDisconnected(120), - BsmDCDC1DCConverterHardwareCurrentOrVoltageFault(121), BmsDCDC1DCConverterCrystalOscillatorCircuitInvalidation(122), BmsDCDC1DCConverterResetCircuitInvalidation(123), BmsDCDC1DCConverterSamplingCircuitInvalidation(124), - BmsDCDC1DCConverterDigitalIOCircuitInvalidation(125), BmsDCDC1DCConverterPWMCircuitInvalidation(126), BmsDCDC1DCConverterX5045CircuitInvalidation(127), BmsDCDC1DCConverterCANCircuitInvalidation(128), - BmsDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(129), BmsDCDC1DCConverterPowerCircuitInvalidation(130), BmsDCDC1DCConverterCPUInvalidation(131), BmsDCDC1DCConverterTINT0InterruptInvalidation(132), - BmsDCDC1DCConverterADCInterruptInvalidation(133), BmsDCDC1DCConverterCAPITN4InterruptInvalidation(134), BmsDCDC1DCConverterCAPINT6InterruptInvalidation(135), BmsDCDC1DCConverterT3PINTinterruptInvalidation(136), - BmsDCDC1DCConverterT4PINTinterruptInvalidation(137), BmsDCDC1DCConverterPDPINTAInterruptInvalidation(138), BmsDCDC1DCConverterT1PINTInterruptInvalidationSecond(139), BmsDCDC1DCConverterRESVInterruptInvalidation(140), - BmsDCDC1DCConverter100usTaskInvalidation(141), BmsDCDC1DCConverterClockInvalidation(142), BmsDCDC1DCConverterEMSMemoryInvalidation(143), BmsDCDC1DCConverterExteriorCommunicationInvalidation(144), BmsDCDC1DCConverterIOInterfaceInvalidation(145), - BmsDCDC1DCConverterInputVoltageBoundFault(146), BmsDCDC1DCConverterOutterVoltageBoundFault(147), BmsDCDC1DCConverterOutputVoltageBoundFault(148), BmsDCDC1DCConverterInductCurrentBoundFault(149), BmsDCDC1DCConverterInputCurrentBoundFault(150), - BmsDCDC1DCConverterOutputCurrentBoundFault(151), BmsDCDC1DCReactorOverTemperature(152), BmsDCDC1DCIGBTOverTemperature(153), BmsDCDC1DCConverterChanel3OverTemperature(154), BmsDCDC1DCConverterChanel4OverTemperature(155), - BmsDCDC1DCConverterChanel5OverTemperature(156), BmsDCDC1DCConverterChanel6OverTemperature(157), BmsDCDC1DCConverterChanel7OverTemperature(158), BmsDCDC1DCConverterChanel8OverTemperature(159), BmsDCDC1DCReactorTemperatureSamplingInvalidation(160), - BmsDCDC1DCIGBTTemperatureSamplingInvalidation(161), BmsDCDC1DCConverterChanel3TemperatureSamplingInvalidation(162), BmsDCDC1DCConverterChanel4TemperatureSamplingInvalidation(163), BmsDCDC1DCConverterChanel5TemperatureSamplingInvalidation(164), - BmsDCDC1DCConverterChanel6TemperatureSamplingInvalidation(165), BmsDCDC1DCConverterChanel7TemperatureSamplingInvalidation(166), BmsDCDC1DCConverterChanel8TemperatureSamplingInvalidation(167), BmsDCDC1DCConverterInductanceCurrentSamplingInvalidation(168), - BmsDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(169), BmsDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(170), BmsDCDC1InsulationInspectionFault(171), BmsDCDC1NegContactorCloseUnsuccessly(172), - BmsDCDC1NegContactorCutWhenRunning(173), PvDCDCHighVoltageSideOfDCConverterUndervoltage(174), PvDCDCHighVoltageSideOfDCConverterOvervoltage(175), PvDCDCLowVoltageSideOfDCConverterUndervoltage(176), PvDCDCLowVoltageSideOfDCConverterOvervoltage(177), - PvDCDCHighVoltageSideOfDCConverterOvercurrentFault(178), PvDCDCLowVoltageSideOfDCConverterOvercurrentFault(179), PvDCDCDCConverterIGBTFault(180), PvDCDCDCConverterPrechargeUnmet(181), PvDCDCBECUCommunicationDisconnected(182), - PvDCDCDCConverterCommunicationDisconnected(183), PvDCDCCurrentConfigurationOverRange(184), PvDCDCTheBatteryRequestStop(185), PvDCDCOvercurrentRelayFault(186), PvDCDCLightningProtectionDeviceFault(187), - PvDCDCDCConverterPriamaryContactorDisconnectedAbnormally(188), PvDCDCDCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(189), PvDCDCDCConvetorEEPROMAbnormity1(190), PvDCDCDCConvetorEEPROMAbnormity1Second(191), PvDCDCEDCConvetorEEPROMAbnormity1(192), - PvDCDCDCConvertorGeneralOverload(193), PvDCDCDCShortCircuit(194), PvDCDCPeakPulseCurrentProtection(195), PvDCDCDCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(196), PvDCDCEffectivePulseValueOverhigh(197), PvDCDCDCConverteSevereOverload(198), - PvDCDCDCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(199), PvDCDCDCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(200), PvDCDCDCConvetorPrechargeContactorCloseFailed(201), PvDCDCDCConvetorMainContactorCloseFailed(202), - PvDCDCACContactorStateAbnormityOfDCConvetor(203), PvDCDCDCConvetorEmergencyStop(204), PvDCDCDCConverterChargingGunDisconnected(205), PvDCDCDCCurrentAbnormityBeforeDCConvetorWork(206), PvDCDCFuSeDisconnected(207), PvDCDCDCConverterHardwareCurrentOrVoltageFault(208), - PvDCDCDCConverterCrystalOscillatorCircuitInvalidation(209), PvDCDCDCConverterResetCircuitInvalidation(210), PvDCDCDCConverterSamplingCircuitInvalidation(211), PvDCDCDCConverterDigitalIOCircuitInvalidation(212), PvDCDCDCConverterPWMCircuitInvalidation(213), - PvDCDCDCConverterX5045CircuitInvalidation(214), PvDCDCDCConverterCANCircuitInvalidation(215), PvDCDCDCConverterSoftwareANDHardwareProtectionCircuitInvalidation(216), PvDCDCDCConverterPowerCircuitInvalidation(217), PvDCDCDCConverterCPUInvalidation(218), - PvDCDCDCConverterTINT0InterruptInvalidation(219), PvDCDCDCConverterADCInterruptInvalidation(220), PvDCDCDCConverterCAPITN4InterruptInvalidation(221), PvDCDCDCConverterCAPINT6InterruptInvalidation(222), PvDCDCDCConverterT3PINTinterruptInvalidation(223), - PvDCDCDCConverterT4PINTinterruptInvalidation(224), PvDCDCConverterPDPINTAInterruptInvalidation(225), PvDCDCConverterT1PINTInterruptInvalidationSecond(226), PvDCDCConverterRESVInterruptInvalidation(227), PvDCDCConverter100usTaskInvalidation(228), - PvDCDCConverterClockInvalidation(229), PvDCDCConverterEMSMemoryInvalidation(230), PvDCDCConverterExteriorCommunicationInvalidation(231), PvDCDCConverterIOInterfaceInvalidation(232), PvDCDCConverterInputVoltageBoundFault(233), - PvDCDCConverterOutterVoltageBoundFault(234), PvDCDCConverterOutputVoltageBoundFault(235), PvDCDCConverterInductCurrentBoundFault(236), PvDCDCConverterInputCurrentBoundFault(237), PvDCDCConverterOutputCurrentBoundFault(238), PvDCDCDCReactorOverTemperature(239), - PvDCDCDCIGBTOverTemperature(240), PvDCDCDCConverterChanel3OverTemperature(241), PvDCDCDCConverterChanel4OverTemperature(242), PvDCDCDCConverterChanel5OverTemperature(243), PvDCDCDCConverterChanel6OverTemperature(244), - PvDCDCDCConverterChanel7OverTemperature(245), PvDCDCDCConverterChanel8OverTemperature(246), PvDCDCDCReactorTemperatureSamplingInvalidation(247), PvDCDCDCIGBTTemperatureSamplingInvalidation(248), PvDCDCDCConverterChanel3TemperatureSamplingInvalidation(249), - PvDCDCDCConverterChanel4TemperatureSamplingInvalidation(250), PvDCDCDCConverterChanel5TemperatureSamplingInvalidation(251), PvDCDCDCConverterChanel6TemperatureSamplingInvalidation(252), PvDCDCDCConverterChanel7TemperatureSamplingInvalidation(253), - PvDCDCDCConverterChanel8TemperatureSamplingInvalidation(254), PvDCDCDCConverterInductanceCurrentSamplingInvalidation(255), PvDCDCCurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(256), PvDCDCVoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(257), - PvDCDCInsulationInspectionFault(258), PvDCDCNegContactorCloseUnsuccessly(259), PvDCDCNegContactorCutWhenRunning(260), PvDCDC1HighVoltageSideOfDCConverterUndervoltage(261), PvDCDC1HighVoltageSideOfDCConverterOvervoltage(262), PvDCDC1LowVoltageSideOfDCConverterUndervoltage(263), - PvDCDC1LowVoltageSideOfDCConverterOvervoltage(264), PvDCDC1HighVoltageSideOfDCConverterOvercurrentFault(265), PvDCDC1LowVoltageSideOfDCConverterOvercurrentFault(266), PvDCDC1DCConverterIGBTFault(267), PvDCDC1DCConverterPrechargeUnmet(268), PvDCDC1BECUCommunicationDisconnected(269), - PvDCDC1DCConverterCommunicationDisconnected(270), PvDCDC1CurrentConfigurationOverRange(271), PvDCDC1TheBatteryRequestStop(272), PvDCDC1OvercurrentRelayFault(273), PvDCDC1LightningProtectionDeviceFault(274), PvDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(275), - PvDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(276), PvDCDC1DCConvetorEEPROMAbnormity1(277), PvDCDC1DCConvetorEEPROMAbnormity1Second(278), PvDCDC1EDCConvetorEEPROMAbnormity1(279), PvDCDC1DCConvertorGeneralOverload(280), PvDCDC1DCShortCircuit(281), - PvDCDC1PeakPulseCurrentProtection(282), PvDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(283), PvDCDC1EffectivePulseValueOverhigh(284), PvDCDC1DCConverteSevereOverload(285), PvDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(286), - PvDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(287), PvDCDC1DCConvetorPrechargeContactorCloseFailed(288), PvDCDC1DCConvetorMainContactorCloseFailed(289), PvDCDC1ACContactorStateAbnormityOfDCConvetor(290), PvDCDC1DCConvetorEmergencyStop(291), - PvDCDC1DCConverterChargingGunDisconnected(292), PvDCDC1DCCurrentAbnormityBeforeDCConvetorWork(293), PvDCDC1FuSeDisconnected(294), PvDCDC1DCConverterHardwareCurrentOrVoltageFault(295), PvDCDC1DCConverterCrystalOscillatorCircuitInvalidation(296), PvDCDC1DCConverterResetCircuitInvalidation(297), - PvDCDC1DCConverterSamplingCircuitInvalidation(298), PvDCDC1DCConverterDigitalIOCircuitInvalidation(299), PvDCDC1DCConverterPWMCircuitInvalidation(300), PvDCDC1DCConverterX5045CircuitInvalidation(301), PvDCDC1DCConverterCANCircuitInvalidation(302), - PvDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(303), PvDCDC1DCConverterPowerCircuitInvalidation(304), PvDCDC1DCConverterCPUInvalidation(305), PvDCDC1DCConverterTINT0InterruptInvalidation(306), PvDCDC1DCConverterADCInterruptInvalidation(307), - PvDCDC1DCConverterCAPITN4InterruptInvalidation(308), PvDCDC1DCConverterCAPINT6InterruptInvalidation(309), PvDCDC1DCConverterT3PINTinterruptInvalidation(310), PvDCDC1DCConverterT4PINTinterruptInvalidation(311), PvDCDC1DCConverterPDPINTAInterruptInvalidation(312), - PvDCDC1DCConverterT1PINTInterruptInvalidationSecond(313), PvDCDC1DCConverterRESVInterruptInvalidation(314), PvDCDC1DCConverter100usTaskInvalidation(315), PvDCDC1DCConverterClockInvalidation(316), PvDCDC1DCConverterEMSMemoryInvalidation(317), - PvDCDC1DCConverterExteriorCommunicationInvalidation(318), PvDCDC1DCConverterIOInterfaceInvalidation(319), PvDCDC1DCConverterInputVoltageBoundFault(320), PvDCDC1DCConverterOutterVoltageBoundFault(321), PvDCDC1DCConverterOutputVoltageBoundFault(322), - PvDCDC1DCConverterInductCurrentBoundFault(323), PvDCDC1DCConverterInputCurrentBoundFault(324), PvDCDC1DCConverterOutputCurrentBoundFault(325), PvDCDC1DCReactorOverTemperature(326), PvDCDC1DCIGBTOverTemperature(327), PvDCDC1DCConverterChanel3OverTemperature(328), - PvDCDC1DCConverterChanel4OverTemperature(329), PvDCDC1DCConverterChanel5OverTemperature(330), PvDCDC1DCConverterChanel6OverTemperature(331), PvDCDC1DCConverterChanel7OverTemperature(332), PvDCDC1DCConverterChanel8OverTemperature(333), PvDCDC1DCReactorTemperatureSamplingInvalidation(334), - PvDCDC1DCIGBTTemperatureSamplingInvalidation(335), PvDCDC1DCConverterChanel3TemperatureSamplingInvalidation(336), PvDCDC1DCConverterChanel4TemperatureSamplingInvalidation(337), PvDCDC1DCConverterChanel5TemperatureSamplingInvalidation(338), PvDCDC1DCConverterChanel6TemperatureSamplingInvalidation(339), - PvDCDC1DCConverterChanel7TemperatureSamplingInvalidation(340), PvDCDC1DCConverterChanel8TemperatureSamplingInvalidation(341), PvDCDC1DCConverterInductanceCurrentSamplingInvalidation(342), PvDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(343), - PvDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(344), PvDCDC1InsulationInspectionFault(345), PvDCDC1NegContactorCloseUnsuccessly(346), PvDCDC1NegContactorCutWhenRunning(347); - - private final int value; - - private FaultCharger(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.FaultEnum; + +public enum FaultCharger implements FaultEnum{ + HighVoltageSideOfDCConverterUndervoltage(0), HighVoltageSideOfDCConverterOvervoltage(1), LowVoltageSideOfDCConverterUndervoltage(2), + LowVoltageSideOfDCConverterOvervoltage(3), HighVoltageSideOfDCConverterOvercurrentFault(4), LowVoltageSideOfDCConverterOvercurrentFault(5), DCConverterIGBTFault(6), DCConverterPrechargeUnmet(7), + BECUCommunicationDisconnected(8), DCConverterCommunicationDisconnected(9), CurrentConfigurationOverRange(10), TheBatteryRequestStop(11), OvercurrentRelayFault(12), LightningProtectionDeviceFault(13), + DCConverterPriamaryContactorDisconnectedAbnormally(14), DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(15), DCConvetorEEPROMAbnormity1(16), DCConvetorEEPROMAbnormity1Second(17), + EDCConvetorEEPROMAbnormity1(18), DCConvertorGeneralOverload(19), DCShortCircuit(20), PeakPulseCurrentProtection(21), DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(22), EffectivePulseValueOverhigh(23), + DCConverteSevereOverload(24), DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(25), DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(26), DCConvetorPrechargeContactorCloseFailed(27), + DCConvetorMainContactorCloseFailed(28), ACContactorStateAbnormityOfDCConvetor(29), DCConvetorEmergencyStop(30), DCConverterChargingGunDisconnected(31), DCCurrentAbnormityBeforeDCConvetorWork(32), FuSeDisconnected(33), + DCConverterHardwareCurrentOrVoltageFault(34), DCConverterCrystalOscillatorCircuitInvalidation(35), DCConverterResetCircuitInvalidation(36), DCConverterSamplingCircuitInvalidation(37), + DCConverterDigitalIOCircuitInvalidation(38), DCConverterPWMCircuitInvalidation(39), DCConverterX5045CircuitInvalidation(40), DCConverterCANCircuitInvalidation(41), DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(42), + DCConverterPowerCircuitInvalidation(43), DCConverterCPUInvalidation(44), DCConverterTINT0InterruptInvalidation(45), DCConverterADCInterruptInvalidation(46), DCConverterCAPITN4InterruptInvalidation(47), + DCConverterCAPINT6InterruptInvalidation(48), DCConverterT3PINTinterruptInvalidation(49), DCConverterT4PINTinterruptInvalidation(50), DCConverterPDPINTAInterruptInvalidation(51), DCConverterT1PINTInterruptInvalidation(52), + DCConverterRESVInterruptInvalidation(53), DCConverter100usTaskInvalidation(54), DCConverterClockInvalidation(55), DCConverterEMSMemoryInvalidation(56), DCConverterExteriorCommunicationInvalidation(57), + DCConverterIOInterfaceInvalidation(58), DCConverterInputVoltageBoundFault(59), DCConverterOutterVoltageBoundFault(60), DCConverterOutputVoltageBoundFault(61), DCConverterInductCurrentBoundFault(62), + DCConverterInputCurrentBoundFault(63), DCConverterOutputCurrentBoundFault(64), DCReactorOverTemperature(65), DCIGBTOverTemperature(66), DCConverterChanel3OverTemperature(67), DCConverterChanel4OverTemperature(68), + DCConverterChanel5OverTemperature(69), DCConverterChanel6OverTemperature(70), DCConverterChanel7OverTemperature(71), DCConverterChanel8OverTemperature(72), DCReactorTemperatureSamplingInvalidation(73), DCIGBTTemperatureSamplingInvalidation(74), + DCConverterChanel3TemperatureSamplingInvalidation(75), DCConverterChanel4TemperatureSamplingInvalidation(76), DCConverterChanel5TemperatureSamplingInvalidation(77), DCConverterChanel6TemperatureSamplingInvalidation(78), + DCConverterChanel7TemperatureSamplingInvalidation(79), DCConverterChanel8TemperatureSamplingInvalidation(80), DCConverterInductanceCurrentSamplingInvalidation(81), CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(82), + VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(83), InsulationInspectionFault(84), NegContactorCloseUnsuccessly(85), NegContactorCutWhenRunning(86), BmsDCDC1HighVoltageSideOfDCConverterUndervoltage(87), + BmsDCDC1HighVoltageSideOfDCConverterOvervoltage(88), BmsDCDC1LowVoltageSideOfDCConverterUndervoltage(89), BmsDCDC1LowVoltageSideOfDCConverterOvervoltage(90), BmsDCDC1HighVoltageSideOfDCConverterOvercurrentFault(91), + BmsDCDC1LowVoltageSideOfDCConverterOvercurrentFault(92), BmsDCDC1DCConverterIGBTFault(93), BmsDCDC1DCConverterPrechargeUnmet(94), BmsDCDC1BECUCommunicationDisconnected(95), BmsDCDC1DCConverterCommunicationDisconnected(96), + BmsDCDC1CurrentConfigurationOverRange(97), BmsDCDC1TheBatteryRequestStop(98), BmsDCDC1OvercurrentRelayFault(99), BmsDCDC1LightningProtectionDeviceFault(100), BmsDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(101), + BmsDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(102), BmsDCDC1DCConvetorEEPROMAbnormity1(103), BmsDCDC1DCConvetorEEPROMAbnormity1Second(104), BmsDCDC1EDCConvetorEEPROMAbnormity1(105), BsmDCDC1DCConvertorGeneralOverload(106), + BsmDCDC1DCShortCircuit(107), BsmDCDC1PeakPulseCurrentProtection(108), BsmDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(109), BsmDCDC1EffectivePulseValueOverhigh(110), BsmDCDC1DCConverteSevereOverload(111), + BsmDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(112), BsmDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(113), BsmDCDC1DCConvetorPrechargeContactorCloseFailed(114), BsmDCDC1DCConvetorMainContactorCloseFailed(115), + BsmDCDC1ACContactorStateAbnormityOfDCConvetor(116), BsmDCDC1DCConvetorEmergencyStop(117), BsmDCDC1DCConverterChargingGunDisconnected(118), BsmDCDC1DCCurrentAbnormityBeforeDCConvetorWork(119), BsmDCDC1FuSeDisconnected(120), + BsmDCDC1DCConverterHardwareCurrentOrVoltageFault(121), BmsDCDC1DCConverterCrystalOscillatorCircuitInvalidation(122), BmsDCDC1DCConverterResetCircuitInvalidation(123), BmsDCDC1DCConverterSamplingCircuitInvalidation(124), + BmsDCDC1DCConverterDigitalIOCircuitInvalidation(125), BmsDCDC1DCConverterPWMCircuitInvalidation(126), BmsDCDC1DCConverterX5045CircuitInvalidation(127), BmsDCDC1DCConverterCANCircuitInvalidation(128), + BmsDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(129), BmsDCDC1DCConverterPowerCircuitInvalidation(130), BmsDCDC1DCConverterCPUInvalidation(131), BmsDCDC1DCConverterTINT0InterruptInvalidation(132), + BmsDCDC1DCConverterADCInterruptInvalidation(133), BmsDCDC1DCConverterCAPITN4InterruptInvalidation(134), BmsDCDC1DCConverterCAPINT6InterruptInvalidation(135), BmsDCDC1DCConverterT3PINTinterruptInvalidation(136), + BmsDCDC1DCConverterT4PINTinterruptInvalidation(137), BmsDCDC1DCConverterPDPINTAInterruptInvalidation(138), BmsDCDC1DCConverterT1PINTInterruptInvalidationSecond(139), BmsDCDC1DCConverterRESVInterruptInvalidation(140), + BmsDCDC1DCConverter100usTaskInvalidation(141), BmsDCDC1DCConverterClockInvalidation(142), BmsDCDC1DCConverterEMSMemoryInvalidation(143), BmsDCDC1DCConverterExteriorCommunicationInvalidation(144), BmsDCDC1DCConverterIOInterfaceInvalidation(145), + BmsDCDC1DCConverterInputVoltageBoundFault(146), BmsDCDC1DCConverterOutterVoltageBoundFault(147), BmsDCDC1DCConverterOutputVoltageBoundFault(148), BmsDCDC1DCConverterInductCurrentBoundFault(149), BmsDCDC1DCConverterInputCurrentBoundFault(150), + BmsDCDC1DCConverterOutputCurrentBoundFault(151), BmsDCDC1DCReactorOverTemperature(152), BmsDCDC1DCIGBTOverTemperature(153), BmsDCDC1DCConverterChanel3OverTemperature(154), BmsDCDC1DCConverterChanel4OverTemperature(155), + BmsDCDC1DCConverterChanel5OverTemperature(156), BmsDCDC1DCConverterChanel6OverTemperature(157), BmsDCDC1DCConverterChanel7OverTemperature(158), BmsDCDC1DCConverterChanel8OverTemperature(159), BmsDCDC1DCReactorTemperatureSamplingInvalidation(160), + BmsDCDC1DCIGBTTemperatureSamplingInvalidation(161), BmsDCDC1DCConverterChanel3TemperatureSamplingInvalidation(162), BmsDCDC1DCConverterChanel4TemperatureSamplingInvalidation(163), BmsDCDC1DCConverterChanel5TemperatureSamplingInvalidation(164), + BmsDCDC1DCConverterChanel6TemperatureSamplingInvalidation(165), BmsDCDC1DCConverterChanel7TemperatureSamplingInvalidation(166), BmsDCDC1DCConverterChanel8TemperatureSamplingInvalidation(167), BmsDCDC1DCConverterInductanceCurrentSamplingInvalidation(168), + BmsDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(169), BmsDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(170), BmsDCDC1InsulationInspectionFault(171), BmsDCDC1NegContactorCloseUnsuccessly(172), + BmsDCDC1NegContactorCutWhenRunning(173), PvDCDCHighVoltageSideOfDCConverterUndervoltage(174), PvDCDCHighVoltageSideOfDCConverterOvervoltage(175), PvDCDCLowVoltageSideOfDCConverterUndervoltage(176), PvDCDCLowVoltageSideOfDCConverterOvervoltage(177), + PvDCDCHighVoltageSideOfDCConverterOvercurrentFault(178), PvDCDCLowVoltageSideOfDCConverterOvercurrentFault(179), PvDCDCDCConverterIGBTFault(180), PvDCDCDCConverterPrechargeUnmet(181), PvDCDCBECUCommunicationDisconnected(182), + PvDCDCDCConverterCommunicationDisconnected(183), PvDCDCCurrentConfigurationOverRange(184), PvDCDCTheBatteryRequestStop(185), PvDCDCOvercurrentRelayFault(186), PvDCDCLightningProtectionDeviceFault(187), + PvDCDCDCConverterPriamaryContactorDisconnectedAbnormally(188), PvDCDCDCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(189), PvDCDCDCConvetorEEPROMAbnormity1(190), PvDCDCDCConvetorEEPROMAbnormity1Second(191), PvDCDCEDCConvetorEEPROMAbnormity1(192), + PvDCDCDCConvertorGeneralOverload(193), PvDCDCDCShortCircuit(194), PvDCDCPeakPulseCurrentProtection(195), PvDCDCDCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(196), PvDCDCEffectivePulseValueOverhigh(197), PvDCDCDCConverteSevereOverload(198), + PvDCDCDCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(199), PvDCDCDCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(200), PvDCDCDCConvetorPrechargeContactorCloseFailed(201), PvDCDCDCConvetorMainContactorCloseFailed(202), + PvDCDCACContactorStateAbnormityOfDCConvetor(203), PvDCDCDCConvetorEmergencyStop(204), PvDCDCDCConverterChargingGunDisconnected(205), PvDCDCDCCurrentAbnormityBeforeDCConvetorWork(206), PvDCDCFuSeDisconnected(207), PvDCDCDCConverterHardwareCurrentOrVoltageFault(208), + PvDCDCDCConverterCrystalOscillatorCircuitInvalidation(209), PvDCDCDCConverterResetCircuitInvalidation(210), PvDCDCDCConverterSamplingCircuitInvalidation(211), PvDCDCDCConverterDigitalIOCircuitInvalidation(212), PvDCDCDCConverterPWMCircuitInvalidation(213), + PvDCDCDCConverterX5045CircuitInvalidation(214), PvDCDCDCConverterCANCircuitInvalidation(215), PvDCDCDCConverterSoftwareANDHardwareProtectionCircuitInvalidation(216), PvDCDCDCConverterPowerCircuitInvalidation(217), PvDCDCDCConverterCPUInvalidation(218), + PvDCDCDCConverterTINT0InterruptInvalidation(219), PvDCDCDCConverterADCInterruptInvalidation(220), PvDCDCDCConverterCAPITN4InterruptInvalidation(221), PvDCDCDCConverterCAPINT6InterruptInvalidation(222), PvDCDCDCConverterT3PINTinterruptInvalidation(223), + PvDCDCDCConverterT4PINTinterruptInvalidation(224), PvDCDCConverterPDPINTAInterruptInvalidation(225), PvDCDCConverterT1PINTInterruptInvalidationSecond(226), PvDCDCConverterRESVInterruptInvalidation(227), PvDCDCConverter100usTaskInvalidation(228), + PvDCDCConverterClockInvalidation(229), PvDCDCConverterEMSMemoryInvalidation(230), PvDCDCConverterExteriorCommunicationInvalidation(231), PvDCDCConverterIOInterfaceInvalidation(232), PvDCDCConverterInputVoltageBoundFault(233), + PvDCDCConverterOutterVoltageBoundFault(234), PvDCDCConverterOutputVoltageBoundFault(235), PvDCDCConverterInductCurrentBoundFault(236), PvDCDCConverterInputCurrentBoundFault(237), PvDCDCConverterOutputCurrentBoundFault(238), PvDCDCDCReactorOverTemperature(239), + PvDCDCDCIGBTOverTemperature(240), PvDCDCDCConverterChanel3OverTemperature(241), PvDCDCDCConverterChanel4OverTemperature(242), PvDCDCDCConverterChanel5OverTemperature(243), PvDCDCDCConverterChanel6OverTemperature(244), + PvDCDCDCConverterChanel7OverTemperature(245), PvDCDCDCConverterChanel8OverTemperature(246), PvDCDCDCReactorTemperatureSamplingInvalidation(247), PvDCDCDCIGBTTemperatureSamplingInvalidation(248), PvDCDCDCConverterChanel3TemperatureSamplingInvalidation(249), + PvDCDCDCConverterChanel4TemperatureSamplingInvalidation(250), PvDCDCDCConverterChanel5TemperatureSamplingInvalidation(251), PvDCDCDCConverterChanel6TemperatureSamplingInvalidation(252), PvDCDCDCConverterChanel7TemperatureSamplingInvalidation(253), + PvDCDCDCConverterChanel8TemperatureSamplingInvalidation(254), PvDCDCDCConverterInductanceCurrentSamplingInvalidation(255), PvDCDCCurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(256), PvDCDCVoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(257), + PvDCDCInsulationInspectionFault(258), PvDCDCNegContactorCloseUnsuccessly(259), PvDCDCNegContactorCutWhenRunning(260), PvDCDC1HighVoltageSideOfDCConverterUndervoltage(261), PvDCDC1HighVoltageSideOfDCConverterOvervoltage(262), PvDCDC1LowVoltageSideOfDCConverterUndervoltage(263), + PvDCDC1LowVoltageSideOfDCConverterOvervoltage(264), PvDCDC1HighVoltageSideOfDCConverterOvercurrentFault(265), PvDCDC1LowVoltageSideOfDCConverterOvercurrentFault(266), PvDCDC1DCConverterIGBTFault(267), PvDCDC1DCConverterPrechargeUnmet(268), PvDCDC1BECUCommunicationDisconnected(269), + PvDCDC1DCConverterCommunicationDisconnected(270), PvDCDC1CurrentConfigurationOverRange(271), PvDCDC1TheBatteryRequestStop(272), PvDCDC1OvercurrentRelayFault(273), PvDCDC1LightningProtectionDeviceFault(274), PvDCDC1DCConverterPriamaryContactorDisconnectedAbnormally(275), + PvDCDC1DCDisconnectedAbnormallyOnLowVoltageSideOfDCConvetor(276), PvDCDC1DCConvetorEEPROMAbnormity1(277), PvDCDC1DCConvetorEEPROMAbnormity1Second(278), PvDCDC1EDCConvetorEEPROMAbnormity1(279), PvDCDC1DCConvertorGeneralOverload(280), PvDCDC1DCShortCircuit(281), + PvDCDC1PeakPulseCurrentProtection(282), PvDCDC1DCDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(283), PvDCDC1EffectivePulseValueOverhigh(284), PvDCDC1DCConverteSevereOverload(285), PvDCDC1DCBreakerDisconnectAbnormallyOnHighVoltageSideOfDCConvetor(286), + PvDCDC1DCBreakerDisconnectAbnormallyOnLowVoltageSideOfDCConvetor(287), PvDCDC1DCConvetorPrechargeContactorCloseFailed(288), PvDCDC1DCConvetorMainContactorCloseFailed(289), PvDCDC1ACContactorStateAbnormityOfDCConvetor(290), PvDCDC1DCConvetorEmergencyStop(291), + PvDCDC1DCConverterChargingGunDisconnected(292), PvDCDC1DCCurrentAbnormityBeforeDCConvetorWork(293), PvDCDC1FuSeDisconnected(294), PvDCDC1DCConverterHardwareCurrentOrVoltageFault(295), PvDCDC1DCConverterCrystalOscillatorCircuitInvalidation(296), PvDCDC1DCConverterResetCircuitInvalidation(297), + PvDCDC1DCConverterSamplingCircuitInvalidation(298), PvDCDC1DCConverterDigitalIOCircuitInvalidation(299), PvDCDC1DCConverterPWMCircuitInvalidation(300), PvDCDC1DCConverterX5045CircuitInvalidation(301), PvDCDC1DCConverterCANCircuitInvalidation(302), + PvDCDC1DCConverterSoftwareANDHardwareProtectionCircuitInvalidation(303), PvDCDC1DCConverterPowerCircuitInvalidation(304), PvDCDC1DCConverterCPUInvalidation(305), PvDCDC1DCConverterTINT0InterruptInvalidation(306), PvDCDC1DCConverterADCInterruptInvalidation(307), + PvDCDC1DCConverterCAPITN4InterruptInvalidation(308), PvDCDC1DCConverterCAPINT6InterruptInvalidation(309), PvDCDC1DCConverterT3PINTinterruptInvalidation(310), PvDCDC1DCConverterT4PINTinterruptInvalidation(311), PvDCDC1DCConverterPDPINTAInterruptInvalidation(312), + PvDCDC1DCConverterT1PINTInterruptInvalidationSecond(313), PvDCDC1DCConverterRESVInterruptInvalidation(314), PvDCDC1DCConverter100usTaskInvalidation(315), PvDCDC1DCConverterClockInvalidation(316), PvDCDC1DCConverterEMSMemoryInvalidation(317), + PvDCDC1DCConverterExteriorCommunicationInvalidation(318), PvDCDC1DCConverterIOInterfaceInvalidation(319), PvDCDC1DCConverterInputVoltageBoundFault(320), PvDCDC1DCConverterOutterVoltageBoundFault(321), PvDCDC1DCConverterOutputVoltageBoundFault(322), + PvDCDC1DCConverterInductCurrentBoundFault(323), PvDCDC1DCConverterInputCurrentBoundFault(324), PvDCDC1DCConverterOutputCurrentBoundFault(325), PvDCDC1DCReactorOverTemperature(326), PvDCDC1DCIGBTOverTemperature(327), PvDCDC1DCConverterChanel3OverTemperature(328), + PvDCDC1DCConverterChanel4OverTemperature(329), PvDCDC1DCConverterChanel5OverTemperature(330), PvDCDC1DCConverterChanel6OverTemperature(331), PvDCDC1DCConverterChanel7OverTemperature(332), PvDCDC1DCConverterChanel8OverTemperature(333), PvDCDC1DCReactorTemperatureSamplingInvalidation(334), + PvDCDC1DCIGBTTemperatureSamplingInvalidation(335), PvDCDC1DCConverterChanel3TemperatureSamplingInvalidation(336), PvDCDC1DCConverterChanel4TemperatureSamplingInvalidation(337), PvDCDC1DCConverterChanel5TemperatureSamplingInvalidation(338), PvDCDC1DCConverterChanel6TemperatureSamplingInvalidation(339), + PvDCDC1DCConverterChanel7TemperatureSamplingInvalidation(340), PvDCDC1DCConverterChanel8TemperatureSamplingInvalidation(341), PvDCDC1DCConverterInductanceCurrentSamplingInvalidation(342), PvDCDC1CurrentSamplingInvalidationOnTheLowVoltageSideOfDCConverter(343), + PvDCDC1VoltageSamplingInvalidationOnTheLowVoltageSideOfDCConverter(344), PvDCDC1InsulationInspectionFault(345), PvDCDC1NegContactorCloseUnsuccessly(346), PvDCDC1NegContactorCutWhenRunning(347); + + private final int value; + + private FaultCharger(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/FaultEss.java b/edge/src/io/openems/impl/device/commercial/FaultEss.java index 0e8e7f15c90..2b24d02d4ad 100644 --- a/edge/src/io/openems/impl/device/commercial/FaultEss.java +++ b/edge/src/io/openems/impl/device/commercial/FaultEss.java @@ -1,35 +1,35 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum { - - DCPrechargeContactorCloseUnsuccessfully(0), ACPrechargeContactorCloseUnsuccessfully(1), ACMainContactorCloseUnsuccessfully(2), DCElectricalBreaker1CloseUnsuccessfully(3), - DCMainContactorCloseUnsuccessfully(4), ACBreakerTrip(5), ACMainContactorOpenWhenRunning(6), DCMainContactorOpenWhenRunning(7), ACMainContactorOpenUnsuccessfully(8), - DCElectricalBreaker1OpenUnsuccessfully(9), DCMainContactorOpenUnsuccessfully(10), HardwarePDPFault(11), MasterStopSuddenly(12), DCShortCircuitProtection(13), DCOvervoltageProtection(14), - DCUndervoltageProtection(15), DCInverseNoConnectionProtection(16), DCDisconnectionProtection(17), CommutingVoltageAbnormityProtection(18), DCOvercurrentProtection(19), - Phase1PeakCurrentOverLimitProtection(20), Phase2PeakCurrentOverLimitProtection(21), Phase3PeakCurrentOverLimitProtection(22), Phase1GridVoltageSamplingInvalidation(23), - Phase2VirtualCurrentOverLimitProtection(24), Phase3VirtualCurrentOverLimitProtection(25), Phase1GridVoltageSamplingInvalidation2(26), Phase2ridVoltageSamplingInvalidation(27), - Phase3GridVoltageSamplingInvalidation(28), Phase1InvertVoltageSamplingInvalidation(29), Phase2InvertVoltageSamplingInvalidation(30), Phase3InvertVoltageSamplingInvalidation(31), - ACCurrentSamplingInvalidation(32), DCCurrentSamplingInvalidation(33), Phase1OvertemperatureProtection(34), Phase2OvertemperatureProtection(35), Phase3OvertemperatureProtection(36), - Phase1TemperatureSamplingInvalidation(37), Phase2TemperatureSamplingInvalidation(38), Phase3TemperatureSamplingInvalidation(39), Phase1PrechargeUnmetProtection(40), Phase2PrechargeUnmetProtection(41), - Phase3PrechargeUnmetProtection(42), UnadaptablePhaseSequenceErrorProtection(43), DSPProtection(44), Phase1GridVoltageSevereOvervoltageProtection(45),Phase1GridVoltageGeneralOvervoltageProtection(46), - Phase2GridVoltageSevereOvervoltageProtection(47), Phase2GridVoltageGeneralOvervoltageProtection(48), Phase3GridVoltageSevereOvervoltageProtection(49), Phase3GridVoltageGeneralOvervoltageProtection(50), - Phase1GridVoltageSevereUndervoltageProtection(51), Phase1GridVoltageGeneralUndervoltageProtection(52), Phase2GridVoltageSevereUndervoltageProtection(53), Phase2GridVoltageGeneralUndervoltageProtection(54), - Phase3GridVoltageSevereUndervoltageProtection(55), Phase3GridVoltageGeneralUndervoltageProtection(56), SevereOverfrequncyProtection(57), GeneralOverfrequncyProtection(58), SevereUnderfrequncyProtection(59), - GeneralsUnderfrequncyProtection(60), Phase1Gridloss(61), Phase2Gridloss(62), Phase3Gridloss(63), IslandingProtection(64), Phase1UnderVoltageRideThrough(65), Phase2UnderVoltageRideThrough(66), - Phase3UnderVoltageRideThrough(67), Phase1InverterVoltageSevereOvervoltageProtection(68), Phase1InverterVoltageGeneralOvervoltageProtection(69), Phase2InverterVoltageSevereOvervoltageProtection(70), - Phase2InverterVoltageGeneralOvervoltageProtection(71), Phase3InverterVoltageSevereOvervoltageProtection(72), Phase3InverterVoltageGeneralOvervoltageProtection(73), - InverterPeakVoltageHighProtectionCauseByACDisconnect(74); - - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.FaultEnum; + +public enum FaultEss implements FaultEnum { + + DCPrechargeContactorCloseUnsuccessfully(0), ACPrechargeContactorCloseUnsuccessfully(1), ACMainContactorCloseUnsuccessfully(2), DCElectricalBreaker1CloseUnsuccessfully(3), + DCMainContactorCloseUnsuccessfully(4), ACBreakerTrip(5), ACMainContactorOpenWhenRunning(6), DCMainContactorOpenWhenRunning(7), ACMainContactorOpenUnsuccessfully(8), + DCElectricalBreaker1OpenUnsuccessfully(9), DCMainContactorOpenUnsuccessfully(10), HardwarePDPFault(11), MasterStopSuddenly(12), DCShortCircuitProtection(13), DCOvervoltageProtection(14), + DCUndervoltageProtection(15), DCInverseNoConnectionProtection(16), DCDisconnectionProtection(17), CommutingVoltageAbnormityProtection(18), DCOvercurrentProtection(19), + Phase1PeakCurrentOverLimitProtection(20), Phase2PeakCurrentOverLimitProtection(21), Phase3PeakCurrentOverLimitProtection(22), Phase1GridVoltageSamplingInvalidation(23), + Phase2VirtualCurrentOverLimitProtection(24), Phase3VirtualCurrentOverLimitProtection(25), Phase1GridVoltageSamplingInvalidation2(26), Phase2ridVoltageSamplingInvalidation(27), + Phase3GridVoltageSamplingInvalidation(28), Phase1InvertVoltageSamplingInvalidation(29), Phase2InvertVoltageSamplingInvalidation(30), Phase3InvertVoltageSamplingInvalidation(31), + ACCurrentSamplingInvalidation(32), DCCurrentSamplingInvalidation(33), Phase1OvertemperatureProtection(34), Phase2OvertemperatureProtection(35), Phase3OvertemperatureProtection(36), + Phase1TemperatureSamplingInvalidation(37), Phase2TemperatureSamplingInvalidation(38), Phase3TemperatureSamplingInvalidation(39), Phase1PrechargeUnmetProtection(40), Phase2PrechargeUnmetProtection(41), + Phase3PrechargeUnmetProtection(42), UnadaptablePhaseSequenceErrorProtection(43), DSPProtection(44), Phase1GridVoltageSevereOvervoltageProtection(45),Phase1GridVoltageGeneralOvervoltageProtection(46), + Phase2GridVoltageSevereOvervoltageProtection(47), Phase2GridVoltageGeneralOvervoltageProtection(48), Phase3GridVoltageSevereOvervoltageProtection(49), Phase3GridVoltageGeneralOvervoltageProtection(50), + Phase1GridVoltageSevereUndervoltageProtection(51), Phase1GridVoltageGeneralUndervoltageProtection(52), Phase2GridVoltageSevereUndervoltageProtection(53), Phase2GridVoltageGeneralUndervoltageProtection(54), + Phase3GridVoltageSevereUndervoltageProtection(55), Phase3GridVoltageGeneralUndervoltageProtection(56), SevereOverfrequncyProtection(57), GeneralOverfrequncyProtection(58), SevereUnderfrequncyProtection(59), + GeneralsUnderfrequncyProtection(60), Phase1Gridloss(61), Phase2Gridloss(62), Phase3Gridloss(63), IslandingProtection(64), Phase1UnderVoltageRideThrough(65), Phase2UnderVoltageRideThrough(66), + Phase3UnderVoltageRideThrough(67), Phase1InverterVoltageSevereOvervoltageProtection(68), Phase1InverterVoltageGeneralOvervoltageProtection(69), Phase2InverterVoltageSevereOvervoltageProtection(70), + Phase2InverterVoltageGeneralOvervoltageProtection(71), Phase3InverterVoltageSevereOvervoltageProtection(72), Phase3InverterVoltageGeneralOvervoltageProtection(73), + InverterPeakVoltageHighProtectionCauseByACDisconnect(74); + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/WarningCharger.java b/edge/src/io/openems/impl/device/commercial/WarningCharger.java index 3f5cb977a56..e6016a0a3b4 100644 --- a/edge/src/io/openems/impl/device/commercial/WarningCharger.java +++ b/edge/src/io/openems/impl/device/commercial/WarningCharger.java @@ -1,57 +1,57 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningCharger implements WarningEnum{ - CurrentSamplingChannelAbnormityOnHighVoltageSide(0), CurrentSamplingChannelAbnormityOnLowVoltageSide(1), BmsDCDC1EEPROMParametersOverRange(2),EEPROMParametersOverRange(3), UpdateEEPROMFailed(4), - ReadEEPROMFailed(5), CurrentSamplingChannelAbnormityBeforeInductance(6), ReactorPowerDecreaseCausedByOvertemperature(7), IGBTPowerDecreaseCausedByOvertemperature(8), - TemperatureChanel3PowerDecreaseCausedByOvertemperature(9), TemperatureChanel4PowerDecreaseCausedByOvertemperature(10), TemperatureChanel5PowerDecreaseCausedByOvertemperature(11), - TemperatureChanel6PowerDecreaseCausedByOvertemperature(12), TemperatureChanel7PowerDecreaseCausedByOvertemperature(13), TemperatureChanel8PowerDecreaseCausedByOvertemperature(14), - Fan1StopFailed(15), Fan2StopFailed(16), Fan3StopFailed(17), Fan4StopFailed(18), Fan1StartupFailed(19), Fan2StartupFailed(20), Fan3StartupFailed(21), Fan4StartupFailed(22), - HighVoltageSideOvervoltage(23), HighVoltageSideUndervoltage(24), HighVoltageSideVoltageChangeUnconventionally(25),CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(26), - CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(27), InitialDutyRatioAbnormityBeforeDCConverterWork(28),VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(29), - VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(30), HighVoltageBreakerInspectionAbnormity(31),LowVoltageBreakerInspectionAbnormity(32), BsmDCDC5DCPrechargeContactorInspectionAbnormity(33), - DCPrechargeContactorOpenUnsuccessfully(34), DCMainContactorInspectionAbnormity(35),DCMainContactorOpenUnsuccessfully(36), OutputContactorCloseUnsuccessfully(37), OutputContactorOpenUnsuccessfully(38), - ACMainContactorCloseUnsuccessfully(39), ACMainContactorOpenUnsuccessfully(40),NegContactorOpenUnsuccessfully(41), NegContactorCloseUnsuccessfully(42), NegContactorStateAbnormal(43), - BsmDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(44), BsmDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(45), BsmDCDC1EEPROMParametersOverRange(46), BsmDCDC1UpdateEEPROMFailed(47), - BsmDCDC1ReadEEPROMFailed(48), BsmDCDC1CurrentSamplingChannelAbnormityBeforeInductance(49), BsmDCDC1ReactorPowerDecreaseCausedByOvertemperature(50), BsmDCDC1IGBTPowerDecreaseCausedByOvertemperature(51), - BsmDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(52), BsmDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(53), BsmDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(54), - BsmDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(55), BsmDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(56), BsmDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(57), - BsmDCDC1Fan1StopFailed(58), BsmDCDC1Fan2StopFailed(59), BsmDCDC1Fan3StopFailed(60), BsmDCDC1Fan4StopFailed(61), BsmDCDC1Fan1StartupFailed(62), BsmDCDC1Fan2StartupFailed(63), BsmDCDC1Fan3StartupFailed(64), - BsmDCDC1Fan4StartupFailed(65), BsmDCDC1HighVoltageSideOvervoltage(66), BsmDCDC1HighVoltageSideUndervoltage(67), BsmDCDC1HighVoltageSideVoltageChangeUnconventionally(68), BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(69), - BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(70), BmsDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(71), BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(72), - BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(73), BmsDCDC1HighVoltageBreakerInspectionAbnormity(74), BmsDCDC1LowVoltageBreakerInspectionAbnormity(75), BmsDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(76), - BmsDCDC1DCPrechargeContactorOpenUnsuccessfully(77), BmsDCDC1DCMainContactorInspectionAbnormity(78), BmsDCDC1DCMainContactorOpenUnsuccessfully(79), BmsDCDC1OutputContactorCloseUnsuccessfully(80), - BmsDCDC1OutputContactorOpenUnsuccessfully(81), BmsDCDC1ACMainContactorCloseUnsuccessfully(82), BmsDCDC1ACMainContactorOpenUnsuccessfully(83), BmsDCDC1NegContactorOpenUnsuccessfully(84), - BmsDCDC1NegContactorCloseUnsuccessfully(85), BmsDCDC1NegContactorStateAbnormal(86), PvDCDCCurrentSamplingChannelAbnormityOnHighVoltageSide(87), PvDCDCCurrentSamplingChannelAbnormityOnLowVoltageSide(88), - PvDCDCEEPROMParametersOverRange(89), PvDCDCUpdateEEPROMFailed(90), PvDCDCReadEEPROMFailed(91), PvDCDCCurrentSamplingChannelAbnormityBeforeInductance(92), PvDCDCReactorPowerDecreaseCausedByOvertemperature(93), - PvDCDCIGBTPowerDecreaseCausedByOvertemperature(94), PvDCDCTemperatureChanel3PowerDecreaseCausedByOvertemperature(95), PvDCDCTemperatureChanel4PowerDecreaseCausedByOvertemperature(96), PvDCDCTemperatureChanel5PowerDecreaseCausedByOvertemperature(97), - PvDCDCTemperatureChanel6PowerDecreaseCausedByOvertemperature(98), PvDCDCTemperatureChanel7PowerDecreaseCausedByOvertemperature(99), PvDCDCTemperatureChanel8PowerDecreaseCausedByOvertemperature(100), - PvDCDCFan1StopFailed(101), PvDCDCFan2StopFailed(102), PvDCDCFan3StopFailed(103), PvDCDCFan4StopFailed(104), PvDCDCFan1StartupFailed(105), PvDCDCFan2StartupFailed(106), PvDCDCFan3StartupFailed(107), - PvDCDCFan4StartupFailed(108), PvDCDCHighVoltageSideOvervoltage(109), PvDCDCHighVoltageSideUndervoltage(110), PvDCDCHighVoltageSideVoltageChangeUnconventionally(111), PvDCDCCurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(112), - PvDCDCCurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(113), PvDCDCInitialDutyRatioAbnormityBeforeDCConverterWork(114), PvDCDCVoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(115), - PvDCDCVoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(116), PvDCDCHighVoltageBreakerInspectionAbnormity(117), PvDCDCLowVoltageBreakerInspectionAbnormity(118), PvDCDCBsmDCDC5DCPrechargeContactorInspectionAbnormity(119), - PvDCDCDCPrechargeContactorOpenUnsuccessfully(120), PvDCDCDCMainContactorInspectionAbnormity(121), PvDCDCDCMainContactorOpenUnsuccessfully(122), PvDCDCOutputContactorCloseUnsuccessfully(123), PvDCDCOutputContactorOpenUnsuccessfully(124), - PvDCDCACMainContactorCloseUnsuccessfully(125), PvDCDCACMainContactorOpenUnsuccessfully(126), PvDCDCNegContactorOpenUnsuccessfully(127), PvDCDCNegContactorCloseUnsuccessfully(128), PvDCDCNegContactorStateAbnormal(129), - PvDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(130), PvDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(131), PvDCDC1EEPROMParametersOverRange(132), PvDCDC1UpdateEEPROMFailed(133), PvDCDC1ReadEEPROMFailed(134), - PvDCDC1CurrentSamplingChannelAbnormityBeforeInductance(135), PvDCDC1ReactorPowerDecreaseCausedByOvertemperature(136), PvDCDC1IGBTPowerDecreaseCausedByOvertemperature(137), PvDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(138), - PvDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(139), PvDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(140), PvDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(141), PvDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(142), - PvDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(143), PvDCDC1Fan1StopFailed(144), PvDCDC1Fan2StopFailed(145), PvDCDC1Fan3StopFailed(146), PvDCDC1Fan4StopFailed(147), PvDCDC1Fan1StartupFailed(148), PvDCDC1Fan2StartupFailed(149), - PvDCDC1Fan3StartupFailed(150), PvDCDC1Fan4StartupFailed(151), PvDCDC1HighVoltageSideOvervoltage(152), PvDCDC1HighVoltageSideUndervoltage(153), PvDCDC1HighVoltageSideVoltageChangeUnconventionally(154), PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(155), - PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(156), PvDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(157), PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(158), PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(159), - PvDCDC1HighVoltageBreakerInspectionAbnormity(160), PvDCDC1LowVoltageBreakerInspectionAbnormity(161), PvDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(162), PvDCDC1DCPrechargeContactorOpenUnsuccessfully(163), PvDCDC1DCMainContactorInspectionAbnormity(164), - PvDCDC1DCMainContactorOpenUnsuccessfully(165), PvDCDC1OutputContactorCloseUnsuccessfully(166), PvDCDC1OutputContactorOpenUnsuccessfully(167), PvDCDC1ACMainContactorCloseUnsuccessfully(168), PvDCDC1ACMainContactorOpenUnsuccessfully(169), - PvDCDC1NegContactorOpenUnsuccessfully(170), PvDCDC1NegContactorCloseUnsuccessfully(171), PvDCDC1NegContactorStateAbnormal(172); - - - public final int value; - - private WarningCharger(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.WarningEnum; + +public enum WarningCharger implements WarningEnum{ + CurrentSamplingChannelAbnormityOnHighVoltageSide(0), CurrentSamplingChannelAbnormityOnLowVoltageSide(1), BmsDCDC1EEPROMParametersOverRange(2),EEPROMParametersOverRange(3), UpdateEEPROMFailed(4), + ReadEEPROMFailed(5), CurrentSamplingChannelAbnormityBeforeInductance(6), ReactorPowerDecreaseCausedByOvertemperature(7), IGBTPowerDecreaseCausedByOvertemperature(8), + TemperatureChanel3PowerDecreaseCausedByOvertemperature(9), TemperatureChanel4PowerDecreaseCausedByOvertemperature(10), TemperatureChanel5PowerDecreaseCausedByOvertemperature(11), + TemperatureChanel6PowerDecreaseCausedByOvertemperature(12), TemperatureChanel7PowerDecreaseCausedByOvertemperature(13), TemperatureChanel8PowerDecreaseCausedByOvertemperature(14), + Fan1StopFailed(15), Fan2StopFailed(16), Fan3StopFailed(17), Fan4StopFailed(18), Fan1StartupFailed(19), Fan2StartupFailed(20), Fan3StartupFailed(21), Fan4StartupFailed(22), + HighVoltageSideOvervoltage(23), HighVoltageSideUndervoltage(24), HighVoltageSideVoltageChangeUnconventionally(25),CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(26), + CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(27), InitialDutyRatioAbnormityBeforeDCConverterWork(28),VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(29), + VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(30), HighVoltageBreakerInspectionAbnormity(31),LowVoltageBreakerInspectionAbnormity(32), BsmDCDC5DCPrechargeContactorInspectionAbnormity(33), + DCPrechargeContactorOpenUnsuccessfully(34), DCMainContactorInspectionAbnormity(35),DCMainContactorOpenUnsuccessfully(36), OutputContactorCloseUnsuccessfully(37), OutputContactorOpenUnsuccessfully(38), + ACMainContactorCloseUnsuccessfully(39), ACMainContactorOpenUnsuccessfully(40),NegContactorOpenUnsuccessfully(41), NegContactorCloseUnsuccessfully(42), NegContactorStateAbnormal(43), + BsmDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(44), BsmDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(45), BsmDCDC1EEPROMParametersOverRange(46), BsmDCDC1UpdateEEPROMFailed(47), + BsmDCDC1ReadEEPROMFailed(48), BsmDCDC1CurrentSamplingChannelAbnormityBeforeInductance(49), BsmDCDC1ReactorPowerDecreaseCausedByOvertemperature(50), BsmDCDC1IGBTPowerDecreaseCausedByOvertemperature(51), + BsmDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(52), BsmDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(53), BsmDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(54), + BsmDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(55), BsmDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(56), BsmDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(57), + BsmDCDC1Fan1StopFailed(58), BsmDCDC1Fan2StopFailed(59), BsmDCDC1Fan3StopFailed(60), BsmDCDC1Fan4StopFailed(61), BsmDCDC1Fan1StartupFailed(62), BsmDCDC1Fan2StartupFailed(63), BsmDCDC1Fan3StartupFailed(64), + BsmDCDC1Fan4StartupFailed(65), BsmDCDC1HighVoltageSideOvervoltage(66), BsmDCDC1HighVoltageSideUndervoltage(67), BsmDCDC1HighVoltageSideVoltageChangeUnconventionally(68), BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(69), + BmsDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(70), BmsDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(71), BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(72), + BmsDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(73), BmsDCDC1HighVoltageBreakerInspectionAbnormity(74), BmsDCDC1LowVoltageBreakerInspectionAbnormity(75), BmsDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(76), + BmsDCDC1DCPrechargeContactorOpenUnsuccessfully(77), BmsDCDC1DCMainContactorInspectionAbnormity(78), BmsDCDC1DCMainContactorOpenUnsuccessfully(79), BmsDCDC1OutputContactorCloseUnsuccessfully(80), + BmsDCDC1OutputContactorOpenUnsuccessfully(81), BmsDCDC1ACMainContactorCloseUnsuccessfully(82), BmsDCDC1ACMainContactorOpenUnsuccessfully(83), BmsDCDC1NegContactorOpenUnsuccessfully(84), + BmsDCDC1NegContactorCloseUnsuccessfully(85), BmsDCDC1NegContactorStateAbnormal(86), PvDCDCCurrentSamplingChannelAbnormityOnHighVoltageSide(87), PvDCDCCurrentSamplingChannelAbnormityOnLowVoltageSide(88), + PvDCDCEEPROMParametersOverRange(89), PvDCDCUpdateEEPROMFailed(90), PvDCDCReadEEPROMFailed(91), PvDCDCCurrentSamplingChannelAbnormityBeforeInductance(92), PvDCDCReactorPowerDecreaseCausedByOvertemperature(93), + PvDCDCIGBTPowerDecreaseCausedByOvertemperature(94), PvDCDCTemperatureChanel3PowerDecreaseCausedByOvertemperature(95), PvDCDCTemperatureChanel4PowerDecreaseCausedByOvertemperature(96), PvDCDCTemperatureChanel5PowerDecreaseCausedByOvertemperature(97), + PvDCDCTemperatureChanel6PowerDecreaseCausedByOvertemperature(98), PvDCDCTemperatureChanel7PowerDecreaseCausedByOvertemperature(99), PvDCDCTemperatureChanel8PowerDecreaseCausedByOvertemperature(100), + PvDCDCFan1StopFailed(101), PvDCDCFan2StopFailed(102), PvDCDCFan3StopFailed(103), PvDCDCFan4StopFailed(104), PvDCDCFan1StartupFailed(105), PvDCDCFan2StartupFailed(106), PvDCDCFan3StartupFailed(107), + PvDCDCFan4StartupFailed(108), PvDCDCHighVoltageSideOvervoltage(109), PvDCDCHighVoltageSideUndervoltage(110), PvDCDCHighVoltageSideVoltageChangeUnconventionally(111), PvDCDCCurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(112), + PvDCDCCurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(113), PvDCDCInitialDutyRatioAbnormityBeforeDCConverterWork(114), PvDCDCVoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(115), + PvDCDCVoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(116), PvDCDCHighVoltageBreakerInspectionAbnormity(117), PvDCDCLowVoltageBreakerInspectionAbnormity(118), PvDCDCBsmDCDC5DCPrechargeContactorInspectionAbnormity(119), + PvDCDCDCPrechargeContactorOpenUnsuccessfully(120), PvDCDCDCMainContactorInspectionAbnormity(121), PvDCDCDCMainContactorOpenUnsuccessfully(122), PvDCDCOutputContactorCloseUnsuccessfully(123), PvDCDCOutputContactorOpenUnsuccessfully(124), + PvDCDCACMainContactorCloseUnsuccessfully(125), PvDCDCACMainContactorOpenUnsuccessfully(126), PvDCDCNegContactorOpenUnsuccessfully(127), PvDCDCNegContactorCloseUnsuccessfully(128), PvDCDCNegContactorStateAbnormal(129), + PvDCDC1CurrentSamplingChannelAbnormityOnHighVoltageSide(130), PvDCDC1CurrentSamplingChannelAbnormityOnLowVoltageSide(131), PvDCDC1EEPROMParametersOverRange(132), PvDCDC1UpdateEEPROMFailed(133), PvDCDC1ReadEEPROMFailed(134), + PvDCDC1CurrentSamplingChannelAbnormityBeforeInductance(135), PvDCDC1ReactorPowerDecreaseCausedByOvertemperature(136), PvDCDC1IGBTPowerDecreaseCausedByOvertemperature(137), PvDCDC1TemperatureChanel3PowerDecreaseCausedByOvertemperature(138), + PvDCDC1TemperatureChanel4PowerDecreaseCausedByOvertemperature(139), PvDCDC1TemperatureChanel5PowerDecreaseCausedByOvertemperature(140), PvDCDC1TemperatureChanel6PowerDecreaseCausedByOvertemperature(141), PvDCDC1TemperatureChanel7PowerDecreaseCausedByOvertemperature(142), + PvDCDC1TemperatureChanel8PowerDecreaseCausedByOvertemperature(143), PvDCDC1Fan1StopFailed(144), PvDCDC1Fan2StopFailed(145), PvDCDC1Fan3StopFailed(146), PvDCDC1Fan4StopFailed(147), PvDCDC1Fan1StartupFailed(148), PvDCDC1Fan2StartupFailed(149), + PvDCDC1Fan3StartupFailed(150), PvDCDC1Fan4StartupFailed(151), PvDCDC1HighVoltageSideOvervoltage(152), PvDCDC1HighVoltageSideUndervoltage(153), PvDCDC1HighVoltageSideVoltageChangeUnconventionally(154), PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnHighVoltageSide(155), + PvDCDC1CurrentAbnormityBeforeDCConverterWorkOnLowVoltageSXide(156), PvDCDC1InitialDutyRatioAbnormityBeforeDCConverterWork(157), PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnHighVoltageSide(158), PvDCDC1VoltageAbnormityBeforeDCConverterWorkOnLowVoltageSide(159), + PvDCDC1HighVoltageBreakerInspectionAbnormity(160), PvDCDC1LowVoltageBreakerInspectionAbnormity(161), PvDCDC1BsmDCDC5DCPrechargeContactorInspectionAbnormity(162), PvDCDC1DCPrechargeContactorOpenUnsuccessfully(163), PvDCDC1DCMainContactorInspectionAbnormity(164), + PvDCDC1DCMainContactorOpenUnsuccessfully(165), PvDCDC1OutputContactorCloseUnsuccessfully(166), PvDCDC1OutputContactorOpenUnsuccessfully(167), PvDCDC1ACMainContactorCloseUnsuccessfully(168), PvDCDC1ACMainContactorOpenUnsuccessfully(169), + PvDCDC1NegContactorOpenUnsuccessfully(170), PvDCDC1NegContactorCloseUnsuccessfully(171), PvDCDC1NegContactorStateAbnormal(172); + + + public final int value; + + private WarningCharger(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/commercial/WarningEss.java b/edge/src/io/openems/impl/device/commercial/WarningEss.java index 9ea9d403b9b..2a1e3cb3c9a 100644 --- a/edge/src/io/openems/impl/device/commercial/WarningEss.java +++ b/edge/src/io/openems/impl/device/commercial/WarningEss.java @@ -1,66 +1,66 @@ -package io.openems.impl.device.commercial; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum { - EmergencyStop(0), // - KeyManualStop(1), // - TransformerPhaseBTemperatureSensorInvalidation(2), // - SDMemoryCardInvalidation(4), // - InverterCommunicationAbnormity(5), // - BatteryStackCommunicationAbnormity(6), // - MultifunctionalAmmeterCommunicationAbnormity(7), // - RemoteCommunicationAbnormity(8), // - PVDC1CommunicationAbnormity(9), // - PVDC2CommunicationAbnormity(10), // - TransformerSevereOvertemperature(11), // - DCPrechargeContactorInspectionAbnormity(12), // - DCBreaker1InspectionAbnormity(13), // - DCBreaker2InspectionAbnormity(14), // - ACPrechargeContactorInspectionAbnormity(15), // - ACMainontactorInspectionAbnormity(16), // - ACBreakerInspectionAbnormity(17), // - DCBreaker1CloseUnsuccessfully(18), // - DCBreaker2CloseUnsuccessfully(19), // - ControlSignalCloseAbnormallyInspectedBySystem(20), // - ControlSignalOpenAbnormallyInspectedBySystem(21), // - NeutralWireContactorCloseUnsuccessfully(22), // - NeutralWireContactorOpenUnsuccessfully(23), // - WorkDoorOpen(24), // - Emergency1Stop(25), // - ACBreakerCloseUnsuccessfully(26), // - ControlSwitchStop(27), // - GeneralOverload(28), SevereOverload(29), // - BatteryCurrentOverLimit(30), // - PowerDecreaseCausedByOvertemperature(31), // - InverterGeneralOvertemperature(32), // - ACThreePhaseCurrentUnbalance(33), // - RestoreFactorySettingUnsuccessfully(34), // - PoleBoardInvalidation(35), // - SelfInspectionFailed(36), // - ReceiveBMSFaultAndStop(37), // - RefrigerationEquipmentinvalidation(38), // - LargeTemperatureDifferenceAmongIGBTThreePhases(39), // - EEPROMParametersOverRange(40), // - EEPROMParametersBackupFailed(41), // - DCBreakerCloseunsuccessfully(42), // - CommunicationBetweenInverterAndBSMUDisconnected(43), // - CommunicationBetweenInverterAndMasterDisconnected(44), // - CommunicationBetweenInverterAndUCDisconnected(45), // - BMSStartOvertimeControlledByPCS(46), // - BMSStopOvertimeControlledByPCS(47), // - SyncSignalInvalidation(48), // - SyncSignalContinuousCaputureFault(49), // - SyncSignalSeveralTimesCaputureFault(50); - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.commercial; + +import io.openems.api.channel.thingstate.WarningEnum; + +public enum WarningEss implements WarningEnum { + EmergencyStop(0), // + KeyManualStop(1), // + TransformerPhaseBTemperatureSensorInvalidation(2), // + SDMemoryCardInvalidation(4), // + InverterCommunicationAbnormity(5), // + BatteryStackCommunicationAbnormity(6), // + MultifunctionalAmmeterCommunicationAbnormity(7), // + RemoteCommunicationAbnormity(8), // + PVDC1CommunicationAbnormity(9), // + PVDC2CommunicationAbnormity(10), // + TransformerSevereOvertemperature(11), // + DCPrechargeContactorInspectionAbnormity(12), // + DCBreaker1InspectionAbnormity(13), // + DCBreaker2InspectionAbnormity(14), // + ACPrechargeContactorInspectionAbnormity(15), // + ACMainontactorInspectionAbnormity(16), // + ACBreakerInspectionAbnormity(17), // + DCBreaker1CloseUnsuccessfully(18), // + DCBreaker2CloseUnsuccessfully(19), // + ControlSignalCloseAbnormallyInspectedBySystem(20), // + ControlSignalOpenAbnormallyInspectedBySystem(21), // + NeutralWireContactorCloseUnsuccessfully(22), // + NeutralWireContactorOpenUnsuccessfully(23), // + WorkDoorOpen(24), // + Emergency1Stop(25), // + ACBreakerCloseUnsuccessfully(26), // + ControlSwitchStop(27), // + GeneralOverload(28), SevereOverload(29), // + BatteryCurrentOverLimit(30), // + PowerDecreaseCausedByOvertemperature(31), // + InverterGeneralOvertemperature(32), // + ACThreePhaseCurrentUnbalance(33), // + RestoreFactorySettingUnsuccessfully(34), // + PoleBoardInvalidation(35), // + SelfInspectionFailed(36), // + ReceiveBMSFaultAndStop(37), // + RefrigerationEquipmentinvalidation(38), // + LargeTemperatureDifferenceAmongIGBTThreePhases(39), // + EEPROMParametersOverRange(40), // + EEPROMParametersBackupFailed(41), // + DCBreakerCloseunsuccessfully(42), // + CommunicationBetweenInverterAndBSMUDisconnected(43), // + CommunicationBetweenInverterAndMasterDisconnected(44), // + CommunicationBetweenInverterAndUCDisconnected(45), // + BMSStartOvertimeControlledByPCS(46), // + BMSStopOvertimeControlledByPCS(47), // + SyncSignalInvalidation(48), // + SyncSignalContinuousCaputureFault(49), // + SyncSignalSeveralTimesCaputureFault(50); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/mini/FaultEss.java b/edge/src/io/openems/impl/device/mini/FaultEss.java index 7cb96a7ec7c..f2bbc53b75d 100644 --- a/edge/src/io/openems/impl/device/mini/FaultEss.java +++ b/edge/src/io/openems/impl/device/mini/FaultEss.java @@ -1,28 +1,28 @@ -package io.openems.impl.device.mini; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum{ - ControlCurrentOverload100Percent(0), ControlCurrentOverload110Percent(1), ControlCurrentOverload150Percent(2), ControlCurrentOverload200Percent(3), - ControlCurrentOverload120Percent(4), ControlCurrentOverload300Percent(5), ControlTransientLoad300Percent(6), GridOverCurrent(7), LockingWaveformTooManyTimes(8), - InverterVoltageZeroDriftError(9), GridVoltageZeroDriftError(10), ControlCurrentZeroDriftError(11), InverterCurrentZeroDriftError(12), GridCurrentZeroDriftError(13), - PDPProtection(14), HardwareControlCurrentProtection(15), HardwareACVoltProtection(16), HardwareDCCurrentProtection(17), HardwareTemperatureProtection(18), - NoCapturingSignal(19), DCOvervoltage(20), DCDisconnected(21), InverterUndervoltage(22), InverterOvervoltage(23), CurrentSensorFail(24), VoltageSensorFail(25), - PowerUncontrollable(26), CurrentUncontrollable(27), FanError(28), PhaseLack(29), InverterRelayFault(30), GridRelayFault(31), ControlPanelOvertemp(32), PowerPanelOvertemp(33), - DCInputOvercurrent(34), CapacitorOvertemp(35), RadiatorOvertemp(36), TransformerOvertemp(37), CombinationCommError(38), EEPROMError(39), LoadCurrentZeroDriftError(40), - CurrentLimitRError(41), PhaseSyncError(42), ExternalPVCurrentZeroDriftError(43), ExternalGridCurrentZeroDriftError(44); - - - - - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.mini; + +import io.openems.api.channel.thingstate.FaultEnum; + +public enum FaultEss implements FaultEnum{ + ControlCurrentOverload100Percent(0), ControlCurrentOverload110Percent(1), ControlCurrentOverload150Percent(2), ControlCurrentOverload200Percent(3), + ControlCurrentOverload120Percent(4), ControlCurrentOverload300Percent(5), ControlTransientLoad300Percent(6), GridOverCurrent(7), LockingWaveformTooManyTimes(8), + InverterVoltageZeroDriftError(9), GridVoltageZeroDriftError(10), ControlCurrentZeroDriftError(11), InverterCurrentZeroDriftError(12), GridCurrentZeroDriftError(13), + PDPProtection(14), HardwareControlCurrentProtection(15), HardwareACVoltProtection(16), HardwareDCCurrentProtection(17), HardwareTemperatureProtection(18), + NoCapturingSignal(19), DCOvervoltage(20), DCDisconnected(21), InverterUndervoltage(22), InverterOvervoltage(23), CurrentSensorFail(24), VoltageSensorFail(25), + PowerUncontrollable(26), CurrentUncontrollable(27), FanError(28), PhaseLack(29), InverterRelayFault(30), GridRelayFault(31), ControlPanelOvertemp(32), PowerPanelOvertemp(33), + DCInputOvercurrent(34), CapacitorOvertemp(35), RadiatorOvertemp(36), TransformerOvertemp(37), CombinationCommError(38), EEPROMError(39), LoadCurrentZeroDriftError(40), + CurrentLimitRError(41), PhaseSyncError(42), ExternalPVCurrentZeroDriftError(43), ExternalGridCurrentZeroDriftError(44); + + + + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/mini/WarningEss.java b/edge/src/io/openems/impl/device/mini/WarningEss.java index acd33576da4..627fd0ce711 100644 --- a/edge/src/io/openems/impl/device/mini/WarningEss.java +++ b/edge/src/io/openems/impl/device/mini/WarningEss.java @@ -1,21 +1,21 @@ -package io.openems.impl.device.mini; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum{ - FailTheSystemShouldBeStopped(0), CommonLowVoltageAlarm(1), CommonHighVoltageAlarm(2), ChargingOverCurrentAlarm(3), - DischargingOverCurrentAlarm(4), OverTemperatureAlarm(5), InteralCommunicationAbnormal(6), GridUndervoltage(7), - GridOvervoltage(8), GridUnderFrequency(9), GridOverFrequency(10), GridPowerSupplyOff(11), GridConditionUnmeet(12), - DCUnderVoltage(13), InputOverResistance(14), CombinationError(15), CommWithInverterError(16), TmeError(17); - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.mini; + +import io.openems.api.channel.thingstate.WarningEnum; + +public enum WarningEss implements WarningEnum{ + FailTheSystemShouldBeStopped(0), CommonLowVoltageAlarm(1), CommonHighVoltageAlarm(2), ChargingOverCurrentAlarm(3), + DischargingOverCurrentAlarm(4), OverTemperatureAlarm(5), InteralCommunicationAbnormal(6), GridUndervoltage(7), + GridOvervoltage(8), GridUnderFrequency(9), GridOverFrequency(10), GridPowerSupplyOff(11), GridConditionUnmeet(12), + DCUnderVoltage(13), InputOverResistance(14), CombinationError(15), CommWithInverterError(16), TmeError(17); + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/minireadonly/FaultEss.java b/edge/src/io/openems/impl/device/minireadonly/FaultEss.java index 877cd7ad862..e5b41e468a7 100644 --- a/edge/src/io/openems/impl/device/minireadonly/FaultEss.java +++ b/edge/src/io/openems/impl/device/minireadonly/FaultEss.java @@ -1,27 +1,27 @@ -package io.openems.impl.device.minireadonly; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum -{ - - BECU1DischargeSevereOvercurrent(0), BECU1ChargeSevereOvercurrent(1), BECU1GeneralUndervoltage(2), BECU1SevereOvervoltage(3), BECU1GeneralOvervoltage(4), BECU1SevereUndervoltage(5), BECU1InsideCANBroken(6), - BECU1GeneralUndervoltageHighCurrentDischarge(7), BECU1BMUError(8), BECU1CurrentSamplingInvalidation(9), BECU1BatteryFail(10), BECU1TemperatureSamplingBroken(11), BECU1Contactor1TestBackIsAbnormalTurnOnAbnormity(12), - BECU1Contactor1TestBackIsAbnormalTurnOffAbnormity(13), BECU1Contactor2TestBackIsAbnormalTurnOnAbnormity(14), BECU1Contactor2TestBackIsAbnormalTurnOffAbnormity(15), BECU1SevereHighTemperatureFault(16), - BECU1HallInvalidation(17), BECU1ContactorInvalidation(18), BECU1OutsideCANBroken(19), BECU1CathodeContactorBroken(20), BECU2DischargeSevereOvercurrent(21), BECU2ChargeSevereOvercurrent(22), BECU2GeneralUndervoltage(23), - BECU2SevereOvervoltage(24), BECU2GeneralOvervoltage(25), BECU2SevereUndervoltage(26), BECU2InsideCANBroken(27), BECU2GeneralUndervoltageHighCurrentDischarge(28), BECU2BMUError(29), BECU2CurrentSamplingInvalidation(30), - BECU2BatteryFail(31), BECU2TemperatureSamplingBroken(32), BECU2Contactor1TestBackIsAbnormalTurnOnAbnormity(33), BECU2Contactor1TestBackIsAbnormalTurnOffAbnormity(34), BECU2Contactor2TestBackIsAbnormalTurnOnAbnormity(35), - BECU2Contactor2TestBackIsAbnormalTurnOffAbnormity(36), BECU2SevereHighTemperatureFault(37), BECU2HallInvalidation(38), BECU2ContactorInvalidation(39), BECU2OutsideCANBroken(40), BECU2CathodeContactorBroken(41), - NoAvailableBatteryGroup(42), StackGeneralLeakage(43), StackSevereLeakage(44), StackStartingFail(45), StackStoppingFail(46), BatteryProtection(47), StackAndGroup1CANCommunicationInterrupt(48), - StackAndGroup2CANCommunicationInterrupt(49); - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.minireadonly; + +import io.openems.api.channel.thingstate.FaultEnum; + +public enum FaultEss implements FaultEnum +{ + + BECU1DischargeSevereOvercurrent(0), BECU1ChargeSevereOvercurrent(1), BECU1GeneralUndervoltage(2), BECU1SevereOvervoltage(3), BECU1GeneralOvervoltage(4), BECU1SevereUndervoltage(5), BECU1InsideCANBroken(6), + BECU1GeneralUndervoltageHighCurrentDischarge(7), BECU1BMUError(8), BECU1CurrentSamplingInvalidation(9), BECU1BatteryFail(10), BECU1TemperatureSamplingBroken(11), BECU1Contactor1TestBackIsAbnormalTurnOnAbnormity(12), + BECU1Contactor1TestBackIsAbnormalTurnOffAbnormity(13), BECU1Contactor2TestBackIsAbnormalTurnOnAbnormity(14), BECU1Contactor2TestBackIsAbnormalTurnOffAbnormity(15), BECU1SevereHighTemperatureFault(16), + BECU1HallInvalidation(17), BECU1ContactorInvalidation(18), BECU1OutsideCANBroken(19), BECU1CathodeContactorBroken(20), BECU2DischargeSevereOvercurrent(21), BECU2ChargeSevereOvercurrent(22), BECU2GeneralUndervoltage(23), + BECU2SevereOvervoltage(24), BECU2GeneralOvervoltage(25), BECU2SevereUndervoltage(26), BECU2InsideCANBroken(27), BECU2GeneralUndervoltageHighCurrentDischarge(28), BECU2BMUError(29), BECU2CurrentSamplingInvalidation(30), + BECU2BatteryFail(31), BECU2TemperatureSamplingBroken(32), BECU2Contactor1TestBackIsAbnormalTurnOnAbnormity(33), BECU2Contactor1TestBackIsAbnormalTurnOffAbnormity(34), BECU2Contactor2TestBackIsAbnormalTurnOnAbnormity(35), + BECU2Contactor2TestBackIsAbnormalTurnOffAbnormity(36), BECU2SevereHighTemperatureFault(37), BECU2HallInvalidation(38), BECU2ContactorInvalidation(39), BECU2OutsideCANBroken(40), BECU2CathodeContactorBroken(41), + NoAvailableBatteryGroup(42), StackGeneralLeakage(43), StackSevereLeakage(44), StackStartingFail(45), StackStoppingFail(46), BatteryProtection(47), StackAndGroup1CANCommunicationInterrupt(48), + StackAndGroup2CANCommunicationInterrupt(49); + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/minireadonly/WarningEss.java b/edge/src/io/openems/impl/device/minireadonly/WarningEss.java index 7031834556d..86c8769db4a 100644 --- a/edge/src/io/openems/impl/device/minireadonly/WarningEss.java +++ b/edge/src/io/openems/impl/device/minireadonly/WarningEss.java @@ -1,38 +1,38 @@ -package io.openems.impl.device.minireadonly; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum -{ - BECU1GeneralChargeOverCurrentAlarm(0), BECU1GeneralDischargeOverCurrentAlarm(1), BECU1ChargeCurrentLimitAlarm(2), BECU1DischargeCurrentLimitAlarm(3), - BECU1GeneralHighVoltageAlarm(4), BECU1GeneralLowVoltageAlarm(5), BECU1AbnormalVoltageChangeAlarm(6), BECU1GeneralHighTemperatureAlarm(7),BECU1GeneralLowTemperatureAlarm(8), - BECU1AbnormalTemperatureChangeAlarm(9), BECU1SevereHighVoltageAlarm(10), BECU1SevereLowVoltageAlarm(11),BECU1SevereLowTemperatureAlarm(12), BECU1SeverveChargeOverCurrentAlarm(13), - BECU1SeverveDischargeOverCurrentAlarm(14), BECU1AbnormalCellCapacityAlarm(15), BECU1BalancedSamplingAlarm(16), BECU1BalancedControlAlarm(17), BECU1HallSensorDoesNotWorkAccurately(18), - BECU1Generalleakage(19), BECU1Severeleakage(20), BECU1Contactor1TurnOnAbnormity(21), BECU1Contactor1TurnOffAbnormity(22), BECU1Contactor2TurnOnAbnormity(23), BECU1Contactor2TurnOffAbnormity(24), - BECU1Contactor4CheckAbnormity(25), BECU1ContactorCurrentUnsafe(26), BECU1Contactor5CheckAbnormity(27), BECU1HighVoltageOffset(28), BECU1LowVoltageOffset(29), BECU1HighTemperatureOffset(30), - BECU2GeneralChargeOverCurrentAlarm(31), BECU2GeneralDischargeOverCurrentAlarm(32), BECU2ChargeCurrentLimitAlarm(33), BECU2DischargeCurrentLimitAlarm(34), BECU2GeneralHighVoltageAlarm(35), - BECU2GeneralLowVoltageAlarm(36), BECU2AbnormalVoltageChangeAlarm(37), BECU2GeneralHighTemperatureAlarm(38), BECU2GeneralLowTemperatureAlarm(39), BECU2AbnormalTemperatureChangeAlarm(40), - BECU2SevereHighVoltageAlarm(41), BECU2SevereLowVoltageAlarm(42), BECU2SevereLowTemperatureAlarm(43), BECU2SeverveChargeOverCurrentAlarm(44), BECU2SeverveDischargeOverCurrentAlarm(45), - BECU2AbnormalCellCapacityAlarm(46), BECU2BalancedSamplingAlarm(47), BECU2BalancedControlAlarm(48), BECU2HallSensorDoesNotWorkAccurately(49), BECU2Generalleakage(50), BECU2Severeleakage(51), - BECU2Contactor1TurnOnAbnormity(52), BECU2Contactor1TurnOffAbnormity(53), BECU2Contactor2TurnOnAbnormity(54), BECU2Contactor2TurnOffAbnormity(55), BECU2Contactor4CheckAbnormity(56), - BECU2ContactorCurrentUnsafe(57), BECU2Contactor5CheckAbnormity(58), BECU2HighVoltageOffset(59), BECU2LowVoltageOffset(60), BECU2HighTemperatureOffset(61), GeneralOvercurrentAlarmAtCellStackCharge(62), - GeneralOvercurrentAlarmAtCellStackDischarge(63), CurrentLimitAlarmAtCellStackCharge(64), CurrentLimitAlarmAtCellStackDischarge(65), GeneralCellStackHighVoltageAlarm(66), GeneralCellStackLowVoltageAlarm(67), - AbnormalCellStackVoltageChangeAlarm(68), GeneralCellStackHighTemperatureAlarm(69), GeneralCellStackLowTemperatureAlarm(70), AbnormalCellStackTemperatureChangeAlarm(71), SevereCellStackHighVoltageAlarm(72), - SevereCellStackLowVoltageAlarm(73), SevereCellStackLowTemperatureAlarm(74), SeverveOverCurrentAlarmAtCellStackDharge(75), SeverveOverCurrentAlarmAtCellStackDischarge(76), AbnormalCellStackCapacityAlarm(77), - TheParameterOfEEPROMInCellStackLoseEffectiveness(78), IsolatingSwitchInConfluenceArkBreak(79), TheCommunicationBetweenCellStackAndTemperatureOfCollectorBreak(80), TheTemperatureOfCollectorFail(81), - HallSensorDoNotWorkAccurately(82), TheCommunicationOfPCSBreak(83), AdvancedChargingOrMainContactorCloseAbnormally(84), AbnormalSampledVoltage(85), AbnormalAdvancedContactorOrAbnormalRS485GalleryOfPCS(86), - AbnormalMainContactor(87), GeneralCellStackLeakage(88), SevereCellStackLeakage(89), SmokeAlarm(90), TheCommunicationWireToAmmeterBreak(91), TheCommunicationWireToDredBreak(92); - - - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.minireadonly; + +import io.openems.api.channel.thingstate.WarningEnum; + +public enum WarningEss implements WarningEnum +{ + BECU1GeneralChargeOverCurrentAlarm(0), BECU1GeneralDischargeOverCurrentAlarm(1), BECU1ChargeCurrentLimitAlarm(2), BECU1DischargeCurrentLimitAlarm(3), + BECU1GeneralHighVoltageAlarm(4), BECU1GeneralLowVoltageAlarm(5), BECU1AbnormalVoltageChangeAlarm(6), BECU1GeneralHighTemperatureAlarm(7),BECU1GeneralLowTemperatureAlarm(8), + BECU1AbnormalTemperatureChangeAlarm(9), BECU1SevereHighVoltageAlarm(10), BECU1SevereLowVoltageAlarm(11),BECU1SevereLowTemperatureAlarm(12), BECU1SeverveChargeOverCurrentAlarm(13), + BECU1SeverveDischargeOverCurrentAlarm(14), BECU1AbnormalCellCapacityAlarm(15), BECU1BalancedSamplingAlarm(16), BECU1BalancedControlAlarm(17), BECU1HallSensorDoesNotWorkAccurately(18), + BECU1Generalleakage(19), BECU1Severeleakage(20), BECU1Contactor1TurnOnAbnormity(21), BECU1Contactor1TurnOffAbnormity(22), BECU1Contactor2TurnOnAbnormity(23), BECU1Contactor2TurnOffAbnormity(24), + BECU1Contactor4CheckAbnormity(25), BECU1ContactorCurrentUnsafe(26), BECU1Contactor5CheckAbnormity(27), BECU1HighVoltageOffset(28), BECU1LowVoltageOffset(29), BECU1HighTemperatureOffset(30), + BECU2GeneralChargeOverCurrentAlarm(31), BECU2GeneralDischargeOverCurrentAlarm(32), BECU2ChargeCurrentLimitAlarm(33), BECU2DischargeCurrentLimitAlarm(34), BECU2GeneralHighVoltageAlarm(35), + BECU2GeneralLowVoltageAlarm(36), BECU2AbnormalVoltageChangeAlarm(37), BECU2GeneralHighTemperatureAlarm(38), BECU2GeneralLowTemperatureAlarm(39), BECU2AbnormalTemperatureChangeAlarm(40), + BECU2SevereHighVoltageAlarm(41), BECU2SevereLowVoltageAlarm(42), BECU2SevereLowTemperatureAlarm(43), BECU2SeverveChargeOverCurrentAlarm(44), BECU2SeverveDischargeOverCurrentAlarm(45), + BECU2AbnormalCellCapacityAlarm(46), BECU2BalancedSamplingAlarm(47), BECU2BalancedControlAlarm(48), BECU2HallSensorDoesNotWorkAccurately(49), BECU2Generalleakage(50), BECU2Severeleakage(51), + BECU2Contactor1TurnOnAbnormity(52), BECU2Contactor1TurnOffAbnormity(53), BECU2Contactor2TurnOnAbnormity(54), BECU2Contactor2TurnOffAbnormity(55), BECU2Contactor4CheckAbnormity(56), + BECU2ContactorCurrentUnsafe(57), BECU2Contactor5CheckAbnormity(58), BECU2HighVoltageOffset(59), BECU2LowVoltageOffset(60), BECU2HighTemperatureOffset(61), GeneralOvercurrentAlarmAtCellStackCharge(62), + GeneralOvercurrentAlarmAtCellStackDischarge(63), CurrentLimitAlarmAtCellStackCharge(64), CurrentLimitAlarmAtCellStackDischarge(65), GeneralCellStackHighVoltageAlarm(66), GeneralCellStackLowVoltageAlarm(67), + AbnormalCellStackVoltageChangeAlarm(68), GeneralCellStackHighTemperatureAlarm(69), GeneralCellStackLowTemperatureAlarm(70), AbnormalCellStackTemperatureChangeAlarm(71), SevereCellStackHighVoltageAlarm(72), + SevereCellStackLowVoltageAlarm(73), SevereCellStackLowTemperatureAlarm(74), SeverveOverCurrentAlarmAtCellStackDharge(75), SeverveOverCurrentAlarmAtCellStackDischarge(76), AbnormalCellStackCapacityAlarm(77), + TheParameterOfEEPROMInCellStackLoseEffectiveness(78), IsolatingSwitchInConfluenceArkBreak(79), TheCommunicationBetweenCellStackAndTemperatureOfCollectorBreak(80), TheTemperatureOfCollectorFail(81), + HallSensorDoNotWorkAccurately(82), TheCommunicationOfPCSBreak(83), AdvancedChargingOrMainContactorCloseAbnormally(84), AbnormalSampledVoltage(85), AbnormalAdvancedContactorOrAbnormalRS485GalleryOfPCS(86), + AbnormalMainContactor(87), GeneralCellStackLeakage(88), SevereCellStackLeakage(89), SmokeAlarm(90), TheCommunicationWireToAmmeterBreak(91), TheCommunicationWireToDredBreak(92); + + + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/refu/FaultEss.java b/edge/src/io/openems/impl/device/refu/FaultEss.java index 8628d7a0188..23cb5528264 100644 --- a/edge/src/io/openems/impl/device/refu/FaultEss.java +++ b/edge/src/io/openems/impl/device/refu/FaultEss.java @@ -1,34 +1,34 @@ -package io.openems.impl.device.refu; - -import io.openems.api.channel.thingstate.FaultEnum; - -public enum FaultEss implements FaultEnum{ - BMSInError(0), BMSInErrorSecond(1), BMSUndervoltage(2), BMSOvercurrent(3), ErrorBMSLimitsNotInitialized(4), ConnectError(5), OvervoltageWarning(6), - UndervoltageWarning(7), OvercurrentWarning(8), BMSReady(9), TREXReady(10), NoEnableBateryGroupOrUsableBatteryGroup(11), NormalLeakageOfBatteryGroup(12), - SeriousLeakageOfBatteryGroup(13), BatteryStartFailure(14), BatteryStopFailure(15), InterruptionOfCANCommunication(16),InterruptionOfCANCommunicationBetweenBatteryGroupAndController(17), - EmergencyStopAbnormalOfAuxiliaryCollector(18), LeakageSelfDetectionOnNegative(19), LeakageSelfDetectionOnPositive(20), SelfDetectionFailureOnBattery(21), - CANCommunicationInterruptionBetweenBatteryGroupAndGroup1(22),CANCommunicationInterruptionBetweenBatteryGroupAndGroup2(23), CANCommunicationInterruptionBetweenBatteryGroupAndGroup3(24), - CANCommunicationInterruptionBetweenBatteryGroupAndGroup4(25), MainContractorAbnormalInBatterySelfDetectGroup1(26), MainContractorAbnormalInBatterySelfDetectGroup2(27), - MainContractorAbnormalInBatterySelfDetectGroup3(28), MainContractorAbnormalInBatterySelfDetectGroup4(29), PreChargeContractorAbnormalOnBatterySelfDetectGroup1(30), - PreChargeContractorAbnormalOnBatterySelfDetectGroup2(31), PreChargeContractorAbnormalOnBatterySelfDetectGroup3(32), PreChargeContractorAbnormalOnBatterySelfDetectGroup4(33), - MainContactFailureOnBatteryControlGroup1(34), MainContactFailureOnBatteryControlGroup2(35), MainContactFailureOnBatteryControlGroup3(36), MainContactFailureOnBatteryControlGroup4(37), - PreChargeFailureOnBatteryControlGroup1(38), PreChargeFailureOnBatteryControlGroup2(39), PreChargeFailureOnBatteryControlGroup3(40), PreChargeFailureOnBatteryControlGroup4(41), - SamplingCircuitAbnormalForBMU(42), PowerCableDisconnectFailure(43), SamplingCircuitDisconnectFailure(44), CANDisconnectForMasterAndSlave(45), SammplingCircuitFailure(46), - SingleBatteryFailure(47), CircuitDetectionAbnormalForMainContactor(48), CircuitDetectionAbnormalForMainContactorSecond(49), CircuitDetectionAbnormalForFancontactor(50), - BMUPowerContactorCircuitDetectionAbnormal(51), CentralContactorCircuitDetectionAbnormal(52), SeriousTemperatureFault(53), CommunicationFaultForSystemController(54), - FrogAlarm(55), FuseFault(56), NormalLeakage(57), SeriousLeakage(58), CANDisconnectionBetweenBatteryGroupAndBatteryStack(59), CentralContactorCircuitOpen(60), - BMUPowerContactorOpen(61); - - - - private final int value; - - private FaultEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.refu; + +import io.openems.api.channel.thingstate.FaultEnum; + +public enum FaultEss implements FaultEnum{ + BMSInError(0), BMSInErrorSecond(1), BMSUndervoltage(2), BMSOvercurrent(3), ErrorBMSLimitsNotInitialized(4), ConnectError(5), OvervoltageWarning(6), + UndervoltageWarning(7), OvercurrentWarning(8), BMSReady(9), TREXReady(10), NoEnableBateryGroupOrUsableBatteryGroup(11), NormalLeakageOfBatteryGroup(12), + SeriousLeakageOfBatteryGroup(13), BatteryStartFailure(14), BatteryStopFailure(15), InterruptionOfCANCommunication(16),InterruptionOfCANCommunicationBetweenBatteryGroupAndController(17), + EmergencyStopAbnormalOfAuxiliaryCollector(18), LeakageSelfDetectionOnNegative(19), LeakageSelfDetectionOnPositive(20), SelfDetectionFailureOnBattery(21), + CANCommunicationInterruptionBetweenBatteryGroupAndGroup1(22),CANCommunicationInterruptionBetweenBatteryGroupAndGroup2(23), CANCommunicationInterruptionBetweenBatteryGroupAndGroup3(24), + CANCommunicationInterruptionBetweenBatteryGroupAndGroup4(25), MainContractorAbnormalInBatterySelfDetectGroup1(26), MainContractorAbnormalInBatterySelfDetectGroup2(27), + MainContractorAbnormalInBatterySelfDetectGroup3(28), MainContractorAbnormalInBatterySelfDetectGroup4(29), PreChargeContractorAbnormalOnBatterySelfDetectGroup1(30), + PreChargeContractorAbnormalOnBatterySelfDetectGroup2(31), PreChargeContractorAbnormalOnBatterySelfDetectGroup3(32), PreChargeContractorAbnormalOnBatterySelfDetectGroup4(33), + MainContactFailureOnBatteryControlGroup1(34), MainContactFailureOnBatteryControlGroup2(35), MainContactFailureOnBatteryControlGroup3(36), MainContactFailureOnBatteryControlGroup4(37), + PreChargeFailureOnBatteryControlGroup1(38), PreChargeFailureOnBatteryControlGroup2(39), PreChargeFailureOnBatteryControlGroup3(40), PreChargeFailureOnBatteryControlGroup4(41), + SamplingCircuitAbnormalForBMU(42), PowerCableDisconnectFailure(43), SamplingCircuitDisconnectFailure(44), CANDisconnectForMasterAndSlave(45), SammplingCircuitFailure(46), + SingleBatteryFailure(47), CircuitDetectionAbnormalForMainContactor(48), CircuitDetectionAbnormalForMainContactorSecond(49), CircuitDetectionAbnormalForFancontactor(50), + BMUPowerContactorCircuitDetectionAbnormal(51), CentralContactorCircuitDetectionAbnormal(52), SeriousTemperatureFault(53), CommunicationFaultForSystemController(54), + FrogAlarm(55), FuseFault(56), NormalLeakage(57), SeriousLeakage(58), CANDisconnectionBetweenBatteryGroupAndBatteryStack(59), CentralContactorCircuitOpen(60), + BMUPowerContactorOpen(61); + + + + private final int value; + + private FaultEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/refu/WarningEss.java b/edge/src/io/openems/impl/device/refu/WarningEss.java index 56f0bfef23b..fd4095eb999 100644 --- a/edge/src/io/openems/impl/device/refu/WarningEss.java +++ b/edge/src/io/openems/impl/device/refu/WarningEss.java @@ -1,29 +1,29 @@ -package io.openems.impl.device.refu; - -import io.openems.api.channel.thingstate.WarningEnum; - -public enum WarningEss implements WarningEnum -{ - NormalChargingOverCurrent(0), CharginigCurrentOverLimit(1), DischargingCurrentOverLimit(2), NormalHighVoltage(3), NormalLowVoltage(4), - AbnormalVoltageVariation(5), NormalHighTemperature(6), NormalLowTemperature(7), AbnormalTemperatureVariation(8), SeriousHighVoltage(9), - SeriousLowVoltage(10), SeriousLowTemperature(11), ChargingSeriousOverCurrent(12), DischargingSeriousOverCurrent(13), AbnormalCapacityAlarm(14), - EEPROMParameterFailure(15), SwitchOfInsideCombinedCabinet(16), ShouldNotBeConnectedToGridDueToTheDCSideCondition(17), - EmergencyStopRequireFromSystemController(18), BatteryGroup1EnableAndNotConnectedToGrid(19), BatteryGroup2EnableAndNotConnectedToGrid(20), - BatteryGroup3EnableAndNotConnectedToGrid(21), BatteryGroup4EnableAndNotConnectedToGrid(22), TheIsolationSwitchOfBatteryGroup1Open(23), - TheIsolationSwitchOfBatteryGroup2Open(24), TheIsolationSwitchOfBatteryGroup3Open(25), TheIsolationSwitchOfBatteryGroup4Open(26), - BalancingSamplingFailureOfBatteryGroup1(27), BalancingSamplingFailureOfBatteryGroup2(28), BalancingSamplingFailureOfBatteryGroup3(29), - BalancingSamplingFailureOfBatteryGroup4(30), BalancingControlFailureOfBatteryGroup1(31), BalancingControlFailureOfBatteryGroup2(32), - BalancingControlFailureOfBatteryGroup3(33), BalancingControlFailureOfBatteryGroup4(34); - - - public final int value; - - private WarningEss(int value) { - this.value = value; - } - - @Override - public int getValue() { - return this.value; - } -} +package io.openems.impl.device.refu; + +import io.openems.api.channel.thingstate.WarningEnum; + +public enum WarningEss implements WarningEnum +{ + NormalChargingOverCurrent(0), CharginigCurrentOverLimit(1), DischargingCurrentOverLimit(2), NormalHighVoltage(3), NormalLowVoltage(4), + AbnormalVoltageVariation(5), NormalHighTemperature(6), NormalLowTemperature(7), AbnormalTemperatureVariation(8), SeriousHighVoltage(9), + SeriousLowVoltage(10), SeriousLowTemperature(11), ChargingSeriousOverCurrent(12), DischargingSeriousOverCurrent(13), AbnormalCapacityAlarm(14), + EEPROMParameterFailure(15), SwitchOfInsideCombinedCabinet(16), ShouldNotBeConnectedToGridDueToTheDCSideCondition(17), + EmergencyStopRequireFromSystemController(18), BatteryGroup1EnableAndNotConnectedToGrid(19), BatteryGroup2EnableAndNotConnectedToGrid(20), + BatteryGroup3EnableAndNotConnectedToGrid(21), BatteryGroup4EnableAndNotConnectedToGrid(22), TheIsolationSwitchOfBatteryGroup1Open(23), + TheIsolationSwitchOfBatteryGroup2Open(24), TheIsolationSwitchOfBatteryGroup3Open(25), TheIsolationSwitchOfBatteryGroup4Open(26), + BalancingSamplingFailureOfBatteryGroup1(27), BalancingSamplingFailureOfBatteryGroup2(28), BalancingSamplingFailureOfBatteryGroup3(29), + BalancingSamplingFailureOfBatteryGroup4(30), BalancingControlFailureOfBatteryGroup1(31), BalancingControlFailureOfBatteryGroup2(32), + BalancingControlFailureOfBatteryGroup3(33), BalancingControlFailureOfBatteryGroup4(34); + + + public final int value; + + private WarningEss(int value) { + this.value = value; + } + + @Override + public int getValue() { + return this.value; + } +} diff --git a/edge/src/io/openems/impl/device/simulator/SimulatorTools.java b/edge/src/io/openems/impl/device/simulator/SimulatorTools.java index b66fea0709a..35aae1e84a4 100644 --- a/edge/src/io/openems/impl/device/simulator/SimulatorTools.java +++ b/edge/src/io/openems/impl/device/simulator/SimulatorTools.java @@ -1,37 +1,37 @@ -package io.openems.impl.device.simulator; - -import java.util.concurrent.ThreadLocalRandom; - -public class SimulatorTools { - - public static long addRandomLong(long value, long min, long max, int delta) { - long random = getRandomLong(delta * -1, delta); - value += random; - if (value > max) { - value = max; - } else if (value < min) { - value = min; - } - return value; - } - - public static long getRandomLong(int min, int max) { - return ThreadLocalRandom.current().nextLong(min, max + 1); - } - - public static double addRandomDouble(double value, double min, double max, double delta) { - double random = getRandomDouble(delta * -1, delta); - value += random; - if (value > max) { - value = max; - } else if (value < min) { - value = min; - } - return value; - } - - public static double getRandomDouble(double min, double max) { - return ThreadLocalRandom.current().nextDouble(min, max); - } - -} +package io.openems.impl.device.simulator; + +import java.util.concurrent.ThreadLocalRandom; + +public class SimulatorTools { + + public static long addRandomLong(long value, long min, long max, int delta) { + long random = getRandomLong(delta * -1, delta); + value += random; + if (value > max) { + value = max; + } else if (value < min) { + value = min; + } + return value; + } + + public static long getRandomLong(int min, int max) { + return ThreadLocalRandom.current().nextLong(min, max + 1); + } + + public static double addRandomDouble(double value, double min, double max, double delta) { + double random = getRandomDouble(delta * -1, delta); + value += random; + if (value > max) { + value = max; + } else if (value < min) { + value = min; + } + return value; + } + + public static double getRandomDouble(double min, double max) { + return ThreadLocalRandom.current().nextDouble(min, max); + } + +} From 9d79367097ef455290c5f327763c9be924e27214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Thu, 8 Mar 2018 09:06:34 +0100 Subject: [PATCH 137/156] remove ChargerLimitationController unimplemented and unused --- .../ChargeLimitationController.java | 74 ------------------- .../controller/chargerlimitation/Charger.java | 30 -------- .../controller/chargerlimitation/Ess.java | 69 ----------------- 3 files changed, 173 deletions(-) delete mode 100644 edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java delete mode 100644 edge/src/io/openems/impl/controller/chargerlimitation/Charger.java delete mode 100644 edge/src/io/openems/impl/controller/chargerlimitation/Ess.java diff --git a/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java b/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java deleted file mode 100644 index 586f0c3c5cf..00000000000 --- a/edge/src/io/openems/impl/controller/chargerlimitation/ChargeLimitationController.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.openems.impl.controller.chargerlimitation; - -import java.util.List; - -import io.openems.api.channel.ConfigChannel; -import io.openems.api.channel.thingstate.ThingStateChannels; -import io.openems.api.controller.Controller; -import io.openems.api.doc.ChannelInfo; -import io.openems.api.doc.ThingInfo; - -@ThingInfo(title = "Limit battery charge from DC", description = "Limits the maximum charge of the battery from DC connected charger.") -public class ChargeLimitationController extends Controller { - - private ThingStateChannels thingState = new ThingStateChannels(this); - /* - * Constructors - */ - - public ChargeLimitationController() { - super(); - } - - public ChargeLimitationController(String thingId) { - super(thingId); - } - - /* - * Config - */ - @ChannelInfo(title = "Ess", description = "Sets the Ess devices.", type = Ess.class) - public ConfigChannel ess = new ConfigChannel("ess", this); - - @ChannelInfo(title = "Chargers", description = "Sets the chargers.", type = Charger.class, isArray = true) - public ConfigChannel> chargers = new ConfigChannel<>("chargers", this); - - /* - * Methods - */ - @Override - public void run() { - // try { - // Ess ess = this.ess.value(); - // List chargers = this.chargers.value(); - // // calculate maximal chargePower - // float power = ess.allowedCharge.value() + ess.getWrittenActivePower(); - // if (power > 0) { - // float maxCurrent = 0l; - // for (Charger c : chargers) { - // maxCurrent += c.nominalCurrent.value(); - // } - // for (Charger c : chargers) { - // c.setPower(power / maxCurrent * c.nominalCurrent.value()); - // } - // ess.setMaxCharge(ess.allowedCharge.value() - power); - // } else { - // for (Charger c : chargers) { - // c.setPower(0); - // } - // } - // } catch (InvalidValueException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } catch (WriteChannelException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - } - - @Override - public ThingStateChannels getStateChannel() { - return this.thingState; - } - -} diff --git a/edge/src/io/openems/impl/controller/chargerlimitation/Charger.java b/edge/src/io/openems/impl/controller/chargerlimitation/Charger.java deleted file mode 100644 index 8e2bda168d7..00000000000 --- a/edge/src/io/openems/impl/controller/chargerlimitation/Charger.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.openems.impl.controller.chargerlimitation; - -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.charger.ChargerNature; - -@IsThingMap(type = ChargerNature.class) -public class Charger extends ThingMap { - - // private WriteChannel current; - // private ReadChannel voltage; - // public ReadChannel nominalCurrent; - - public Charger(ChargerNature thing) { - super(thing); - // current = thing.setMaxCurrent(); - // voltage = thing.getBatteryVoltage(); - // nominalCurrent = thing.getNominalCurrent(); - } - - // public void setPower(float power) throws WriteChannelException, InvalidValueException { - // float calculatedCurrent = power / voltage.value(); - // if (calculatedCurrent > nominalCurrent.value()) { - // calculatedCurrent = nominalCurrent.value(); - // } - // log.info("Set " + calculatedCurrent + " for Charger."); - // current.pushWrite(calculatedCurrent); - // } - -} diff --git a/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java b/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java deleted file mode 100644 index 14b31506e3c..00000000000 --- a/edge/src/io/openems/impl/controller/chargerlimitation/Ess.java +++ /dev/null @@ -1,69 +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.chargerlimitation; - -import io.openems.api.channel.ReadChannel; -import io.openems.api.channel.WriteChannel; -import io.openems.api.controller.IsThingMap; -import io.openems.api.controller.ThingMap; -import io.openems.api.device.nature.ess.AsymmetricEssNature; -import io.openems.api.exception.WriteChannelException; - -@IsThingMap(type = AsymmetricEssNature.class) -public class Ess extends ThingMap { - - public ReadChannel soc; - public ReadChannel allowedCharge; - public WriteChannel setActivePowerL1; - public WriteChannel setActivePowerL2; - public WriteChannel setActivePowerL3; - public ReadChannel activePowerL1; - public ReadChannel activePowerL2; - public ReadChannel activePowerL3; - - public Ess(AsymmetricEssNature thing) { - super(thing); - this.soc = thing.soc().required(); - this.allowedCharge = thing.allowedCharge().required(); - this.setActivePowerL1 = thing.setActivePowerL1(); - this.setActivePowerL2 = thing.setActivePowerL2(); - this.setActivePowerL3 = thing.setActivePowerL3(); - this.activePowerL1 = thing.activePowerL1(); - this.activePowerL2 = thing.activePowerL2(); - this.activePowerL3 = thing.activePowerL3(); - } - - public Long getWrittenActivePower() { - long writePower = setActivePowerL1.peekWrite().orElse(0l) + setActivePowerL2.peekWrite().orElse(0l) - + setActivePowerL3.peekWrite().orElse(0l); - long currentPower = activePowerL1.valueOptional().orElse(0L) + activePowerL2.valueOptional().orElse(0L) - + activePowerL3.valueOptional().orElse(0L); - return writePower < currentPower ? writePower : currentPower; - } - - public void setMaxCharge(float power) throws WriteChannelException { - power *= -1; - setActivePowerL1.pushWriteMin((long) (power / 3f)); - setActivePowerL2.pushWriteMin((long) (power / 3f)); - setActivePowerL3.pushWriteMin((long) (power / 3f)); - } - -} From c928ce30fe420f9d4c2e7bca20012660243b3ff0 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Mon, 12 Mar 2018 10:18:31 +0100 Subject: [PATCH 138/156] Add symmetric FixValueController template + sort templates --- .../{ => asymmetric}/FixValueActivePowerController.json | 2 +- .../{ => symmetric}/BalancingBandgapController.json | 0 .../symmetric/FixValueActivePowerController.json | 8 ++++++++ 3 files changed, 9 insertions(+), 1 deletion(-) rename edge/template/controller/{ => asymmetric}/FixValueActivePowerController.json (94%) rename edge/template/controller/{ => symmetric}/BalancingBandgapController.json (100%) create mode 100644 edge/template/controller/symmetric/FixValueActivePowerController.json diff --git a/edge/template/controller/FixValueActivePowerController.json b/edge/template/controller/asymmetric/FixValueActivePowerController.json similarity index 94% rename from edge/template/controller/FixValueActivePowerController.json rename to edge/template/controller/asymmetric/FixValueActivePowerController.json index b54abc3dc81..a2bc8960fea 100644 --- a/edge/template/controller/FixValueActivePowerController.json +++ b/edge/template/controller/asymmetric/FixValueActivePowerController.json @@ -7,4 +7,4 @@ "activePowerL1": 2000, "activePowerL2": 2000, "activePowerL3": 2000 -} +} \ No newline at end of file diff --git a/edge/template/controller/BalancingBandgapController.json b/edge/template/controller/symmetric/BalancingBandgapController.json similarity index 100% rename from edge/template/controller/BalancingBandgapController.json rename to edge/template/controller/symmetric/BalancingBandgapController.json diff --git a/edge/template/controller/symmetric/FixValueActivePowerController.json b/edge/template/controller/symmetric/FixValueActivePowerController.json new file mode 100644 index 00000000000..691f1133706 --- /dev/null +++ b/edge/template/controller/symmetric/FixValueActivePowerController.json @@ -0,0 +1,8 @@ +{ + "class": "io.openems.impl.controller.symmetric.fixvalue.FixValueActivePowerController", + "esss": [ + "ess0" + ], + "priority": 150, + "activePower": 2000 +} From fcb393a908592ff01e1dfb23e2ef53cadd258b78 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Mon, 12 Mar 2018 10:18:56 +0100 Subject: [PATCH 139/156] Add simple ModbusMaster tool for tests --- tools/pom.xml | 5 +++++ tools/src/tools/ModbusMaster.java | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tools/src/tools/ModbusMaster.java diff --git a/tools/pom.xml b/tools/pom.xml index ecd4c677290..68a9f7570c6 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -38,5 +38,10 @@ gson 2.8.2 + + com.ghgande + j2mod + 2.0-rc4 +
        \ No newline at end of file diff --git a/tools/src/tools/ModbusMaster.java b/tools/src/tools/ModbusMaster.java new file mode 100644 index 00000000000..7a7737e2ab7 --- /dev/null +++ b/tools/src/tools/ModbusMaster.java @@ -0,0 +1,23 @@ +package tools; + +import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster; +import com.ghgande.j2mod.modbus.procimg.Register; + +public class ModbusMaster { + + public static void main(String[] args) { + ModbusTCPMaster master = new ModbusTCPMaster("10.0.10.230", 502, 10000, true); + try { + master.connect(); + Register[] registers = master.readMultipleRegisters(40000, 1); + for (Register register : registers) { + System.out.println(register); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + master.disconnect(); + } + } + +} From 98d6f0fe1d3ef1e91856a7bdceab9fd3b974413a Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Mon, 12 Mar 2018 11:35:29 +0100 Subject: [PATCH 140/156] Improve backend configuration - default: use relative path for configFile (./resources/config.json) - to run backend with custom config file, execute like "java -DconfigFile="./config.json" -jar openems-backend.jar" - JsonPersistenceManager holds some default configuration options (i.e. to improve log output formatting) that are not stored in the config file - Show error when config file does not exist --- .../BackendApp.bndrun | 2 +- .../resources/.gitignore | 1 + .../resources/config.template.json | 2 + .../backend/application/BackendApp.java | 8 ---- .../src/io/openems/common/config/Config.java | 10 +++++ .../io/openems/common/config/ConfigUtils.java | 39 +++++-------------- .../common/config/JsonPersistenceManager.java | 21 ++++++++++ 7 files changed, 44 insertions(+), 39 deletions(-) create mode 100644 io.openems.backend.application/resources/.gitignore create mode 100644 io.openems.backend.application/resources/config.template.json diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index fa2067c4210..05952964123 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -16,7 +16,7 @@ JPM-Command: openems-backend osgi.identity;filter:='(osgi.identity=io.openems.backend.timedata.influx.provider)',\ osgi.identity;filter:='(osgi.identity=io.openems.backend.uiwebsocket.impl.provider)' -runproperties: \ - configFile=C:/openems-config/backend.json,\ + configFile=./resources/config.json,\ org.ops4j.pax.logging.service.frameworkEventsLogLevel="DISABLED" -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' diff --git a/io.openems.backend.application/resources/.gitignore b/io.openems.backend.application/resources/.gitignore new file mode 100644 index 00000000000..0cffcb348ff --- /dev/null +++ b/io.openems.backend.application/resources/.gitignore @@ -0,0 +1 @@ +config.json \ No newline at end of file diff --git a/io.openems.backend.application/resources/config.template.json b/io.openems.backend.application/resources/config.template.json new file mode 100644 index 00000000000..2c63c085104 --- /dev/null +++ b/io.openems.backend.application/resources/config.template.json @@ -0,0 +1,2 @@ +{ +} diff --git a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java index cece9d1e6cb..8ed2fe12bd6 100644 --- a/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java +++ b/io.openems.backend.application/src/io/openems/backend/application/BackendApp.java @@ -1,27 +1,19 @@ package io.openems.backend.application; -import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.config.ConfigUtils; - @Component() public class BackendApp { private final Logger log = LoggerFactory.getLogger(BackendApp.class); - @Reference - private ConfigurationAdmin configAdmin; - @Activate void activate() { log.debug("Activate BackendApp"); - ConfigUtils.configureLogging(configAdmin); } @Deactivate diff --git a/io.openems.common/src/io/openems/common/config/Config.java b/io.openems.common/src/io/openems/common/config/Config.java index 65772eb4790..676a2bee4db 100644 --- a/io.openems.common/src/io/openems/common/config/Config.java +++ b/io.openems.common/src/io/openems/common/config/Config.java @@ -7,9 +7,15 @@ public class Config extends Hashtable { // private final Logger log = LoggerFactory.getLogger(Config.class); private final String pid; + private final boolean doNotStore; public Config(String pid) { + this(pid, false); + } + + public Config(String pid, boolean doNotStore) { this.pid = pid; + this.doNotStore = doNotStore; this.put("service.pid", pid); } @@ -17,6 +23,10 @@ public String getPid() { return pid; } + public boolean isDoNotStore() { + return doNotStore; + } + private static final long serialVersionUID = 1L; @Override diff --git a/io.openems.common/src/io/openems/common/config/ConfigUtils.java b/io.openems.common/src/io/openems/common/config/ConfigUtils.java index 954a3d8f690..0170c492c69 100644 --- a/io.openems.common/src/io/openems/common/config/ConfigUtils.java +++ b/io.openems.common/src/io/openems/common/config/ConfigUtils.java @@ -5,11 +5,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Map.Entry; - -import org.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; - -import java.util.Hashtable; import java.util.TreeMap; import com.google.gson.Gson; @@ -24,33 +19,12 @@ public class ConfigUtils { private final static Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - public static synchronized void configureLogging(ConfigurationAdmin configAdmin) { - Configuration configuration; - try { - configuration = configAdmin.getConfiguration("org.ops4j.pax.logging", null); - final Hashtable log4jProps = new Hashtable(); - log4jProps.put("log4j.rootLogger", "DEBUG, CONSOLE"); - log4jProps.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); - log4jProps.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); - log4jProps.put("log4j.appender.CONSOLE.layout.ConversionPattern", - "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); - // set minimum log levels for some verbose packages - log4jProps.put("log4j.logger.org.eclipse.osgi", "WARN"); - log4jProps.put("log4j.logger.org.apache.felix.configadmin", "INFO"); - log4jProps.put("log4j.logger.sun.net.www.protocol.http.HttpURLConnection", "INFO"); - configuration.update(log4jProps); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - protected static synchronized JsonObject readConfigFromFile(Path path) throws Exception { - try { - String config = new String(Files.readAllBytes(path), DEFAULT_CHARSET); - return JsonUtils.parse(config).getAsJsonObject(); - } catch (Exception e) { - return new JsonObject(); + if (!Files.exists(path)) { + throw new IOException("Configuration file [" + path.toAbsolutePath() + "] not found!"); } + String config = new String(Files.readAllBytes(path), DEFAULT_CHARSET); + return JsonUtils.parse(config).getAsJsonObject(); } protected static synchronized void writeConfigToFile(Path path, TreeMap configs) @@ -58,6 +32,11 @@ protected static synchronized void writeConfigToFile(Path path, TreeMap entry : configs.entrySet()) { + // ignore configs that should not be stored + if (entry.getValue().isDoNotStore()) { + continue; + } + JsonObject jSub = new JsonObject(); // sort map by key to be able to write the json sorted TreeMap sortedSub = new TreeMap<>(); diff --git a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java index bdcaed0baa2..701bd66e65f 100644 --- a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java +++ b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java @@ -31,6 +31,9 @@ public class JsonPersistenceManager implements PersistenceManager { @Activate void activate() { + // Load default configuration + loadDefaultConfig(); + // read Json from file JsonObject jConfig; try { @@ -132,4 +135,22 @@ private void saveConfigMapToFile() { } } } + + private void loadDefaultConfig() { + log.info("Load default config"); + synchronized (this.configs) { + Config log4j = new Config("org.ops4j.pax.logging", true); + log4j.put("log4j.rootLogger", "DEBUG, CONSOLE"); + log4j.put("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); + log4j.put("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); + log4j.put("log4j.appender.CONSOLE.layout.ConversionPattern", + "%d{ISO8601} [%-8.8t] %-5p [%-30.30c] - %m%n"); + // set minimum log levels for some verbose packages + log4j.put("log4j.logger.org.eclipse.osgi", "WARN"); + log4j.put("log4j.logger.org.apache.felix.configadmin", "INFO"); + log4j.put("log4j.logger.sun.net.www.protocol.http.HttpURLConnection", "INFO"); + this.configs.put(log4j.getPid(), log4j); + } + log.info("Finished Load default config"); + } } From 25169467f9ecbf385b1e763cedf80e0d80e74c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Mar 2018 14:04:47 +0100 Subject: [PATCH 141/156] write Power only, if a Limitation has been applied --- .../symmetric/SymmetricPowerClusterImpl.java | 311 +++++++++--------- .../power/symmetric/SymmetricPowerImpl.java | 34 +- 2 files changed, 175 insertions(+), 170 deletions(-) diff --git a/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java index 51fefcb9494..7efb3b553bf 100644 --- a/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java +++ b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerClusterImpl.java @@ -120,169 +120,172 @@ public void applyLimitation(Limitation limit) throws PowerException { } private void setPower() { - synchronized (this.ess) { - Point p = reduceToZero(); - setGeometry(p); - long activePower = (long) p.getCoordinate().x; - long reactivePower = (long) p.getCoordinate().y; - long socSum = 0; - for (SymmetricEssNature ess : this.ess) { - socSum += ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0); - } - if (activePower > 0) { - /* - * Discharge - */ - // sort ess by useableSoc asc - Collections.sort(ess, (a, b) -> { - return (int) ((a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0)) - - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0))); - }); - for (int i = 0; i < ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minP = activePower; - for (int j = i + 1; j < this.ess.size(); j++) { - if (this.ess.get(j).soc().valueOptional().orElse(0L) - - this.ess.get(j).minSoc().valueOptional().orElse(0) > 0) { - minP -= this.ess.get(j).getPower().getMaxP().orElse(0L); - } - } - if (minP < 0) { - minP = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxP = ess.getPower().getMaxP().orElse(0L); - if (activePower < maxP) { - maxP = activePower; - } - double diff = maxP - minP; + if (dynamicLimitations.size() > 0) { + synchronized (this.ess) { + Point p = reduceToZero(); + setGeometry(p); + long activePower = (long) p.getCoordinate().x; + long reactivePower = (long) p.getCoordinate().y; + long socSum = 0; + for (SymmetricEssNature ess : this.ess) { + socSum += ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0); + } + if (activePower > 0) { /* - * weight the range of possible power by the useableSoc - * if the useableSoc is negative the ess will be charged + * Discharge */ - long power = (long) (Math.ceil(minP + diff / socSum - * (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); - PEqualLimitation limit = new PEqualLimitation(ess.getPower()); - limit.setP(power); - try { - ess.getPower().applyLimitation(limit); - activePower -= power; - } catch (PowerException e) { - log.error("Failed to set activePower on " + ess.id()); - } - } - } else { - /* - * Charge - */ - /* - * sort ess by 100 - useabelSoc - * 100 - 90 = 10 - * 100 - 45 = 55 - * 100 - (- 5) = 105 - * => ess with negative useableSoc will be charged much more then one with positive useableSoc - */ - Collections.sort(this.ess, (a, b) -> { - return (int) ((100 - (a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0))) - - (100 - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0)))); - }); - for (int i = 0; i < this.ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minP = activePower; - for (int j = i + 1; j < this.ess.size(); j++) { - minP -= this.ess.get(j).getPower().getMinP().orElse(0L); - } - if (minP > 0) { - minP = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxP = ess.getPower().getMinP().orElse(0L); - if (activePower > maxP) { - maxP = activePower; - } - double diff = maxP - minP; - // weight the range of possible power by the useableSoc - long power = (long) Math.floor(minP + diff / (this.ess.size() * 100 - socSum) - * (100 - (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); - PEqualLimitation limit = new PEqualLimitation(ess.getPower()); - limit.setP(power); - try { - ess.getPower().applyLimitation(limit); - activePower -= power; - } catch (PowerException e) { - log.error("Failed to set activePower on " + ess.id()); - } - } - } - - // sort ess by maxNominalPower asc - Collections.sort(ess, (a, b) -> { - return (int) (a.maxNominalPower().valueOptional().orElse(0L) - - b.maxNominalPower().valueOptional().orElse(0L)); - }); - if (reactivePower > 0) { - for (int i = 0; i < ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minQ = reactivePower; - for (int j = i + 1; j < this.ess.size(); j++) { - if (this.ess.get(j).maxNominalPower().valueOptional().orElse(0L) > 0) { - minQ -= this.ess.get(j).getPower().getMaxQ().orElse(0L); + // sort ess by useableSoc asc + Collections.sort(ess, (a, b) -> { + return (int) ((a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0)) + - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0))); + }); + for (int i = 0; i < ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minP = activePower; + for (int j = i + 1; j < this.ess.size(); j++) { + if (this.ess.get(j).soc().valueOptional().orElse(0L) + - this.ess.get(j).minSoc().valueOptional().orElse(0) > 0) { + minP -= this.ess.get(j).getPower().getMaxP().orElse(0L); + } + } + if (minP < 0) { + minP = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxP = ess.getPower().getMaxP().orElse(0L); + if (activePower < maxP) { + maxP = activePower; + } + double diff = maxP - minP; + /* + * weight the range of possible power by the useableSoc + * if the useableSoc is negative the ess will be charged + */ + long power = (long) (Math.ceil(minP + diff / socSum + * (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); + PEqualLimitation limit = new PEqualLimitation(ess.getPower()); + limit.setP(power); + try { + ess.getPower().applyLimitation(limit); + activePower -= power; + } catch (PowerException e) { + log.error("Failed to set activePower on " + ess.id()); } } - if (minQ < 0) { - minQ = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxQ = ess.getPower().getMaxQ().orElse(0L); - if (reactivePower < maxQ) { - maxQ = reactivePower; - } - double diff = maxQ - minQ; + } else { /* - * weight the range of possible power by the useableSoc - * if the useableSoc is negative the ess will be charged + * Charge */ - long power = (long) (Math.ceil( - minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L))); - QEqualLimitation limit = new QEqualLimitation(ess.getPower()); - limit.setQ(power); - try { - ess.getPower().applyLimitation(limit); - reactivePower -= power; - } catch (PowerException e) { - log.error("Failed to set reactivePower on " + ess.id()); + /* + * sort ess by 100 - useabelSoc + * 100 - 90 = 10 + * 100 - 45 = 55 + * 100 - (- 5) = 105 + * => ess with negative useableSoc will be charged much more then one with positive useableSoc + */ + Collections.sort(this.ess, (a, b) -> { + return (int) ((100 + - (a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0))) + - (100 - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0)))); + }); + for (int i = 0; i < this.ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minP = activePower; + for (int j = i + 1; j < this.ess.size(); j++) { + minP -= this.ess.get(j).getPower().getMinP().orElse(0L); + } + if (minP > 0) { + minP = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxP = ess.getPower().getMinP().orElse(0L); + if (activePower > maxP) { + maxP = activePower; + } + double diff = maxP - minP; + // weight the range of possible power by the useableSoc + long power = (long) Math.floor(minP + diff / (this.ess.size() * 100 - socSum) * (100 + - (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0)))); + PEqualLimitation limit = new PEqualLimitation(ess.getPower()); + limit.setP(power); + try { + ess.getPower().applyLimitation(limit); + activePower -= power; + } catch (PowerException e) { + log.error("Failed to set activePower on " + ess.id()); + } } } - } else { - for (int i = 0; i < this.ess.size(); i++) { - SymmetricEssNature ess = this.ess.get(i); - // calculate minimal power needed to fulfill the calculatedPower - long minQ = reactivePower; - for (int j = i + 1; j < this.ess.size(); j++) { - minQ -= this.ess.get(j).getPower().getMinQ().orElse(0L); - } - if (minQ > 0) { - minQ = 0; - } - // check maximal power to avoid larger charges then calculatedPower - long maxQ = ess.getPower().getMinQ().orElse(0L); - if (reactivePower > maxQ) { - maxQ = reactivePower; + + // sort ess by maxNominalPower asc + Collections.sort(ess, (a, b) -> { + return (int) (a.maxNominalPower().valueOptional().orElse(0L) + - b.maxNominalPower().valueOptional().orElse(0L)); + }); + if (reactivePower > 0) { + for (int i = 0; i < ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minQ = reactivePower; + for (int j = i + 1; j < this.ess.size(); j++) { + if (this.ess.get(j).maxNominalPower().valueOptional().orElse(0L) > 0) { + minQ -= this.ess.get(j).getPower().getMaxQ().orElse(0L); + } + } + if (minQ < 0) { + minQ = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxQ = ess.getPower().getMaxQ().orElse(0L); + if (reactivePower < maxQ) { + maxQ = reactivePower; + } + double diff = maxQ - minQ; + /* + * weight the range of possible power by the useableSoc + * if the useableSoc is negative the ess will be charged + */ + long power = (long) (Math.ceil(minQ + + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L))); + QEqualLimitation limit = new QEqualLimitation(ess.getPower()); + limit.setQ(power); + try { + ess.getPower().applyLimitation(limit); + reactivePower -= power; + } catch (PowerException e) { + log.error("Failed to set reactivePower on " + ess.id()); + } } - double diff = maxQ - minQ; - // weight the range of possible power by the useableSoc - long power = (long) Math.floor( - minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L)); - QEqualLimitation limit = new QEqualLimitation(ess.getPower()); - limit.setQ(power); - try { - ess.getPower().applyLimitation(limit); - reactivePower -= power; - } catch (PowerException e) { - log.error("Failed to set reactivePower on " + ess.id()); + } else { + for (int i = 0; i < this.ess.size(); i++) { + SymmetricEssNature ess = this.ess.get(i); + // calculate minimal power needed to fulfill the calculatedPower + long minQ = reactivePower; + for (int j = i + 1; j < this.ess.size(); j++) { + minQ -= this.ess.get(j).getPower().getMinQ().orElse(0L); + } + if (minQ > 0) { + minQ = 0; + } + // check maximal power to avoid larger charges then calculatedPower + long maxQ = ess.getPower().getMinQ().orElse(0L); + if (reactivePower > maxQ) { + maxQ = reactivePower; + } + double diff = maxQ - minQ; + // weight the range of possible power by the useableSoc + long power = (long) Math.floor( + minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L)); + QEqualLimitation limit = new QEqualLimitation(ess.getPower()); + limit.setQ(power); + try { + ess.getPower().applyLimitation(limit); + reactivePower -= power; + } catch (PowerException e) { + log.error("Failed to set reactivePower on " + ess.id()); + } } } } diff --git a/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java index 0664806d470..f1a30f2c9c6 100644 --- a/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java +++ b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerImpl.java @@ -34,9 +34,9 @@ public SymmetricPowerImpl(long maxApparentPower, WriteChannel setActivePow this.dynamicLimitations = new ArrayList<>(); this.setActivePower = setActivePower; this.setReactivePower = setReactivePower; - if(bridge != null) { + if (bridge != null) { bridge.addListener(this); - }else { + } else { log.error("the Bridge is null! the Power Values won't be writte!"); } createBaseGeometry(); @@ -74,20 +74,22 @@ public void applyLimitation(Limitation limit) throws PowerException { } private void writePower() { - Point p = reduceToZero(); - Coordinate c = p.getCoordinate(); - setGeometry(p); - double activePowerDelta = c.x - lastActivePower; - double reactivePowerDelta = c.y - lastReactivePower; - lastActivePower += activePowerDelta/2; - lastReactivePower += reactivePowerDelta/2; - try { - this.setActivePower.pushWrite((long) lastActivePower); - this.setReactivePower.pushWrite((long) lastReactivePower); - setActivePower.shadowCopyAndReset(); - setReactivePower.shadowCopyAndReset(); - } catch (WriteChannelException e) { - log.error("failed to write Power.", e); + if (dynamicLimitations.size() > 0) { + Point p = reduceToZero(); + Coordinate c = p.getCoordinate(); + setGeometry(p); + double activePowerDelta = c.x - lastActivePower; + double reactivePowerDelta = c.y - lastReactivePower; + lastActivePower += activePowerDelta / 2; + lastReactivePower += reactivePowerDelta / 2; + try { + this.setActivePower.pushWrite((long) lastActivePower); + this.setReactivePower.pushWrite((long) lastReactivePower); + setActivePower.shadowCopyAndReset(); + setReactivePower.shadowCopyAndReset(); + } catch (WriteChannelException e) { + log.error("failed to write Power.", e); + } } } From 7ed50e8857ea657e84f105387443658140254665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Mon, 12 Mar 2018 16:51:30 +0100 Subject: [PATCH 142/156] detect if sep= line is present to parse header appropriate --- .../device/simulator/CSVLoadGenerator.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/edge/src/io/openems/impl/device/simulator/CSVLoadGenerator.java b/edge/src/io/openems/impl/device/simulator/CSVLoadGenerator.java index b801321c766..8d8596c7687 100644 --- a/edge/src/io/openems/impl/device/simulator/CSVLoadGenerator.java +++ b/edge/src/io/openems/impl/device/simulator/CSVLoadGenerator.java @@ -23,7 +23,7 @@ public class CSVLoadGenerator implements LoadGenerator { private List values = new ArrayList<>(0); private int count = 0; private int columnPart = 0; - private String separator = ""; + private String separator = ";"; public CSVLoadGenerator(JsonObject config) { super(); @@ -40,11 +40,19 @@ public CSVLoadGenerator(JsonObject config) { try { br = new BufferedReader(new FileReader(this.filepath)); values = br.lines().collect(Collectors.toList()); - String[] str = values.get(0).split("="); - separator = str[str.length - 1]; - str = values.get(1).split(separator); - for (int i = 0; i < str.length; i++) { - if (str[i].equals(columnKey)) { + String[] columnNames; + if (values.get(0).contains("sep=")) { + String[] str = values.get(0).split("="); + separator = str[str.length - 1]; + columnNames = values.get(1).split(separator); + values.remove(1); + values.remove(0); + }else { + columnNames = values.get(0).split(separator); + values.remove(0); + } + for (int i = 0; i < columnNames.length; i++) { + if (columnNames[i].equals(columnKey)) { columnPart = i; } } From c75e2e1f6137dccc256f107ce8f0d5c039c69ae6 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Mar 2018 09:29:12 +0100 Subject: [PATCH 143/156] Implement OnConfigUpdate event and implement FeneconPersistence and WebsocketApi as listeners --- edge/src/io/openems/core/Config.java | 21 +++++++- .../core/utilities/OnConfigUpdate.java | 5 ++ .../api/websocket/WebsocketApiController.java | 20 ++++++++ .../api/websocket/WebsocketApiServer.java | 19 ++++++++ .../fenecon/FeneconPersistence.java | 48 ++++++++++++++----- 5 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 edge/src/io/openems/core/utilities/OnConfigUpdate.java diff --git a/edge/src/io/openems/core/Config.java b/edge/src/io/openems/core/Config.java index a065bfd0c8c..00d8265a322 100644 --- a/edge/src/io/openems/core/Config.java +++ b/edge/src/io/openems/core/Config.java @@ -27,6 +27,7 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Optional; @@ -64,6 +65,7 @@ import io.openems.common.utils.JsonUtils; import io.openems.core.utilities.ConfigUtils; import io.openems.core.utilities.InjectionUtils; +import io.openems.core.utilities.OnConfigUpdate; public class Config implements ChannelChangeListener { @@ -252,7 +254,6 @@ private JsonObject addDefaultConfig(JsonObject jConfig) { * @throws NotImplementedException */ public void writeConfigFile() throws NotImplementedException { - // TODO send config to all attached websockets // get config as json JsonObject jConfig = this.getJson(ConfigFormat.FILE, Role.ADMIN, "en"); @@ -546,12 +547,15 @@ public synchronized JsonObject getJson(ConfigFormat format, Role role, String la */ @Override public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - // TODO: trigger ConfigUpdated event try { writeConfigFile(); } catch (OpenemsException e) { log.error("Config-Error.", e); } + // announce listeners + for(OnConfigUpdate listener : this.onConfigUpdateListeners) { + listener.call(); + } } /** @@ -576,4 +580,17 @@ private Path getConfigBackupFile() { Path backupFile = configFile.getParent().resolve(CONFIG_BACKUP_FILE_NAME); return backupFile; } + + /** + * Listener for Config updates + */ + private final Set onConfigUpdateListeners = new HashSet<>(); + + public void addOnConfigUpdateListener(OnConfigUpdate listener) { + this.onConfigUpdateListeners.add(listener); + } + + public void removeOnConfigUpdateListener(OnConfigUpdate listener) { + this.onConfigUpdateListeners.remove(listener); + } } \ No newline at end of file diff --git a/edge/src/io/openems/core/utilities/OnConfigUpdate.java b/edge/src/io/openems/core/utilities/OnConfigUpdate.java new file mode 100644 index 00000000000..8e97fee0ee6 --- /dev/null +++ b/edge/src/io/openems/core/utilities/OnConfigUpdate.java @@ -0,0 +1,5 @@ +package io.openems.core.utilities; + +public interface OnConfigUpdate { + public void call(); +} diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java index c9b67089286..b738b1cb908 100644 --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiController.java @@ -30,6 +30,9 @@ import io.openems.api.controller.Controller; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.ConfigException; +import io.openems.core.Config; +import io.openems.core.utilities.OnConfigUpdate; import io.openems.core.utilities.api.ApiWorker; @ThingInfo(title = "Websocket-API", description = "Required by OpenEMS-UI.") @@ -43,10 +46,12 @@ public class WebsocketApiController extends Controller implements ChannelChangeL */ public WebsocketApiController() { super(); + this.registerOnConfigUpdateListener(); } public WebsocketApiController(String thingId) { super(thingId); + this.registerOnConfigUpdateListener(); } /* @@ -70,6 +75,21 @@ public WebsocketApiController(String thingId) { * Fields */ private volatile WebsocketApiServer websocketApiServer = null; + /* + * Listen to config updates + */ + OnConfigUpdate onConfigUpdate = () -> { + if(this.websocketApiServer != null) { + this.websocketApiServer.onConfigUpdate(); + } + }; + private void registerOnConfigUpdateListener() { + try { + Config.getInstance().addOnConfigUpdateListener(this.onConfigUpdate); + } catch (ConfigException e) { + log.error(e.getMessage()); + } + } /* * Methods diff --git a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java index 707e696b0de..e6eef96b0e9 100644 --- a/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java +++ b/edge/src/io/openems/impl/controller/api/websocket/WebsocketApiServer.java @@ -24,6 +24,7 @@ import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.UUID; @@ -37,6 +38,7 @@ import io.openems.api.security.User; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; import io.openems.common.utils.SecureRandomSingleton; import io.openems.common.websocket.AbstractWebsocketServer; @@ -44,6 +46,8 @@ import io.openems.common.websocket.LogBehaviour; import io.openems.common.websocket.Notification; import io.openems.common.websocket.WebSocketUtils; +import io.openems.core.Config; +import io.openems.core.ConfigFormat; import io.openems.core.utilities.api.ApiWorker; public class WebsocketApiServer extends AbstractWebsocketServer { @@ -324,4 +328,19 @@ private void disposeHandler(WebSocket websocket) { log.warn("Unable to dispose Handler: " + e.getMessage()); } } + + private final static String DEFAULT_CONFIG_LANGUAGE = "en"; + + public void onConfigUpdate() { + for(UiEdgeWebsocketHandler handler : this.handlers.values()) { + try { + Role role = handler.getUserOpt().get().getRole(); + JsonObject j = DefaultMessages.configQueryReply(new JsonObject(), + Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, role, DEFAULT_CONFIG_LANGUAGE)); + handler.send(j); + } catch (OpenemsException | NoSuchElementException e) { + log.warn(e.getMessage()); + } + } + } } diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index 9a3c28c7e04..ed52ba4b2df 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -45,6 +45,7 @@ import io.openems.api.device.nature.DeviceNature; import io.openems.api.doc.ChannelInfo; import io.openems.api.doc.ThingInfo; +import io.openems.api.exception.ConfigException; import io.openems.api.persistence.Persistence; import io.openems.api.thing.Thing; import io.openems.common.exceptions.OpenemsException; @@ -57,11 +58,11 @@ import io.openems.common.types.StringFieldValue; import io.openems.common.utils.StringUtils; import io.openems.common.websocket.DefaultMessages; -import io.openems.common.websocket.WebSocketUtils; import io.openems.core.Config; import io.openems.core.ConfigFormat; import io.openems.core.Databus; import io.openems.core.ThingRepository; +import io.openems.core.utilities.OnConfigUpdate; // TODO make sure this is registered as ChannelChangeListener also to ConfigChannels @ThingInfo(title = "FENECON Persistence", description = "Establishes the connection to FENECON Cloud.") @@ -99,6 +100,7 @@ public class FeneconPersistence extends Persistence implements ChannelChangeList */ public FeneconPersistence() { this.thingState = new ThingStateChannels(this); + this.reconnectingWebsocket = new ReconnectingWebsocket((websocket) -> { /* * onOpen @@ -109,15 +111,7 @@ public FeneconPersistence() { // Add current status of all channels to queue this.addCurrentValueOfAllChannelsToQueue(); // Send current config - try { - WebSocketUtils.send( // - websocket, // - DefaultMessages.configQueryReply(new JsonObject(), Config.getInstance() - .getJson(ConfigFormat.OPENEMS_UI, Role.ADMIN, DEFAULT_CONFIG_LANGUAGE))); - log.info("Sent config to FENECON persistence."); - } catch (OpenemsException e) { - log.error("Unable to send config: " + e.getMessage()); - } + this.onConfigUpdate.call(); }, () -> { /* * onClose @@ -126,6 +120,27 @@ public FeneconPersistence() { log.error("FENECON persistence closed connection to uri [" + uri.valueOptional().orElse("") + "]" + (proxyInfoOpt.isPresent() ? ", " + proxyInfoOpt.get() : "")); }); + + /* + * Listen to config updates + */ + onConfigUpdate = () -> { + try { + if (reconnectingWebsocket != null) { + reconnectingWebsocket.send(DefaultMessages.configQueryReply(new JsonObject(), + Config.getInstance().getJson(ConfigFormat.OPENEMS_UI, Role.ADMIN, DEFAULT_CONFIG_LANGUAGE))); + } + log.info("Sent config to FENECON persistence."); + } catch (OpenemsException e) { + log.error("Unable to send config: " + e.getMessage()); + } + }; + try { + Config config = Config.getInstance(); + config.addOnConfigUpdateListener(this.onConfigUpdate); + } catch (ConfigException e) { + log.error(e.getMessage()); + } } @Override @@ -138,6 +153,7 @@ public void init() { */ private static final int DEFAULT_CYCLETIME = 10000; private final ReconnectingWebsocket reconnectingWebsocket; + private volatile OnConfigUpdate onConfigUpdate = null; // Queue of data for the next cycle private HashMap> queue = new HashMap<>(); @@ -240,6 +256,12 @@ protected void forever() { @Override protected void dispose() { this.reconnectingWebsocket.dispose(); + try { + Config config = Config.getInstance(); + config.removeOnConfigUpdateListener(this.onConfigUpdate); + } catch (ConfigException e) { + log.error(e.getMessage()); + } } /** @@ -264,9 +286,9 @@ private boolean sendOrLogError(JsonObject j) { * * @return */ - // public EdgeWebsocketHandler getWebsocketHandler() { - // return this.websocketHandler; - // } + // public EdgeWebsocketHandler getWebsocketHandler() { + // return this.websocketHandler; + // } private void increaseCycleTime() { int currentCycleTime = this.getCycleTime(); From 2c35569f3bd82d4a30c5b5b9e26ce5c8e56e105b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Mar 2018 09:37:22 +0100 Subject: [PATCH 144/156] fix perSum powerreduction --- .../core/utilities/AsymmetricPower.java | 206 ++++++++++++------ 1 file changed, 136 insertions(+), 70 deletions(-) diff --git a/edge/src/io/openems/core/utilities/AsymmetricPower.java b/edge/src/io/openems/core/utilities/AsymmetricPower.java index d2e138373a6..c18433f26ef 100644 --- a/edge/src/io/openems/core/utilities/AsymmetricPower.java +++ b/edge/src/io/openems/core/utilities/AsymmetricPower.java @@ -23,7 +23,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -130,11 +132,25 @@ public void reducePower(ReductionType reductionType) throws WriteChannelExceptio new ArrayList(), new ArrayList() }; @SuppressWarnings("unchecked") List[] maxReactivePowerPhase = new List[] { new ArrayList(), new ArrayList(), new ArrayList() }; + Map> activePowerPosChannels = new HashMap<>(); + Map> activePowerNegChannels = new HashMap<>(); + Map> reactivePowerPosChannels = new HashMap<>(); + Map> reactivePowerNegChannels = new HashMap<>(); + long activePowerSum = 0; + long activePowerAsymmetriePosSum = 0; long activePowerPosSum = 0; long activePowerNegSum = 0; - long activePowerSum = 0; long reactivePowerSum = 0; + long reactivePowerAsymmetriePosSum = 0; for (int i = 0; i < 3; i++) { + // Check if active power is already set + if (setActivePower[i].getWriteValue().isPresent()) { + this.activePower[i] = setActivePower[i].getWriteValue().get(); + } + // Check if reactive power is already set + if (setReactivePower[i].getWriteValue().isPresent()) { + this.reactivePower[i] = setReactivePower[i].getWriteValue().get(); + } if (activePower[i] > 0) { activePowerPosSum += activePower[i]; } else { @@ -143,20 +159,26 @@ public void reducePower(ReductionType reductionType) throws WriteChannelExceptio activePowerSum += activePower[i]; reactivePowerSum += reactivePower[i]; } + for (int i = 0; i < 3; i++) { + if (activePower[i] - activePowerSum / 3 > 0) { + activePowerPosChannels.put(i, setActivePower[i]); + activePowerAsymmetriePosSum += activePower[i] - activePowerSum / 3; + } else { + activePowerNegChannels.put(i, setActivePower[i]); + } + if (reactivePower[i] - reactivePowerSum / 3 > 0) { + reactivePowerPosChannels.put(i, setReactivePower[i]); + reactivePowerAsymmetriePosSum += reactivePower[i] - reactivePowerSum / 3; + } else { + reactivePowerNegChannels.put(i, setReactivePower[i]); + } + } try { for (int i = 0; i < 3; i++) { - // Check if active power is already set - if (setActivePower[i].getWriteValue().isPresent()) { - this.activePower[i] = setActivePower[i].getWriteValue().get(); - } - // Check if reactive power is already set - if (setReactivePower[i].getWriteValue().isPresent()) { - this.reactivePower[i] = setReactivePower[i].getWriteValue().get(); - } // set limits by allowed apparent double cosPhi = ControllerUtils.calculateCosPhi(this.activePower[i], this.reactivePower[i]); - long activePower = ControllerUtils.calculateActivePowerFromApparentPower(allowedApparent.value() / 3, - cosPhi); + long activePower = Math.abs( + ControllerUtils.calculateActivePowerFromApparentPower(allowedApparent.value() / 3, cosPhi)); long reactivePower = ControllerUtils.calculateReactivePower(activePower, allowedApparent.value() / 3); maxReactivePowerPhase[i].add(reactivePower); minReactivePowerPhase[i].add(reactivePower * -1); @@ -174,16 +196,19 @@ public void reducePower(ReductionType reductionType) throws WriteChannelExceptio if (setActivePower[i].writeMin().isPresent()) { minActivePowerPhase[i].add(setActivePower[i].writeMin().get()); } - if (this.activePower[i] < 0) { - minActivePowerPhase[i].add(allowedCharge.value() / activePowerNegSum * this.activePower[i]); - } - if (this.activePower[i] > 0) { - maxActivePowerPhase[i].add(allowedDischarge.value() / activePowerPosSum * this.activePower[i]); - } } - switch (reductionType) { case PERSUM: { + // Reduce activePower by allowedCharge/allowedDischarge + long batteryPowerShift = 0; + if (allowedCharge.value() > activePowerSum) { + batteryPowerShift = allowedCharge.value() - activePowerSum; + } else if (allowedDischarge.value() < activePowerSum) { + batteryPowerShift = allowedDischarge.value() - activePowerSum; + } + for (int i = 0; i < 3; i++) { + reducedActivePower[i] -= batteryPowerShift / 3; + } Long[] minActivePowers = new Long[] { Collections.max(minActivePowerPhase[0]), Collections.max(minActivePowerPhase[1]), Collections.max(minActivePowerPhase[2]) }; Long[] maxActivePowers = new Long[] { Collections.min(maxActivePowerPhase[0]), @@ -192,56 +217,113 @@ public void reducePower(ReductionType reductionType) throws WriteChannelExceptio Collections.max(minReactivePowerPhase[1]), Collections.max(minReactivePowerPhase[2]) }; Long[] maxReactivePowers = new Long[] { Collections.min(maxReactivePowerPhase[0]), Collections.min(maxReactivePowerPhase[1]), Collections.min(maxReactivePowerPhase[2]) }; + long activePowerReduction = 0; + long reactivePowerReduction = 0; for (int i = 0; i < 3; i++) { - if (activePower[i] > maxActivePowers[i]) { - setMinMaxValues(maxActivePowers[i], activePower[i], maxActivePowerPhase, minActivePowerPhase, - activePowerSum, i); - } else if (activePower[i] < minActivePowers[i]) { - setMinMaxValues(minActivePowers[i], activePower[i], maxActivePowerPhase, minActivePowerPhase, - activePowerSum, i); + long activepowerDelta = 0; + if (reducedActivePower[i] > maxActivePowers[i]) { + activepowerDelta = maxActivePowers[i] - reducedActivePower[i]; + } else if (reducedActivePower[i] < minActivePowers[i]) { + activepowerDelta = minActivePowers[i] - reducedActivePower[i]; + } + activepowerDelta = Math.abs(activepowerDelta); + if (activePowerNegChannels.containsKey(i) && activePowerNegChannels.size() > 1) { + Map> remainingChannel = new HashMap<>(activePowerNegChannels); + remainingChannel.remove(i); + double reductionPercentage = activepowerDelta / (reducedActivePower[i] - activePowerSum / 3.0); + for (int j : remainingChannel.keySet()) { + activepowerDelta += (reducedActivePower[j] - activePowerSum / 3) * reductionPercentage; + } + } else if (activePowerPosChannels.containsKey(i) && activePowerPosChannels.size() > 1) { + Map> remainingChannel = new HashMap<>(activePowerPosChannels); + remainingChannel.remove(i); + double reductionPercentage = activepowerDelta / (reducedActivePower[i] - activePowerSum / 3.0); + for (int j : remainingChannel.keySet()) { + activepowerDelta += (reducedActivePower[j] - activePowerSum / 3.0) * reductionPercentage; + } + } + activePowerReduction = Math.max(activepowerDelta, activePowerReduction); + long reactivepowerDelta = 0; + if (reducedReactivePower[i] > maxReactivePowers[i]) { + reactivepowerDelta = maxReactivePowers[i] - reducedReactivePower[i]; + } else if (reducedReactivePower[i] < minReactivePowers[i]) { + reactivepowerDelta = minReactivePowers[i] - reducedReactivePower[i]; } - if (reactivePower[i] > maxReactivePowers[i]) { - setMinMaxValues(maxReactivePowers[i], reactivePower[i], maxReactivePowerPhase, - minReactivePowerPhase, reactivePowerSum, i); - } else if (reactivePower[i] < minReactivePowers[i]) { - setMinMaxValues(minReactivePowers[i], reactivePower[i], maxReactivePowerPhase, - minReactivePowerPhase, reactivePowerSum, i); + reactivepowerDelta = Math.abs(reactivepowerDelta); + if (reactivePowerNegChannels.containsKey(i) && reactivePowerNegChannels.size() > 1) { + Map> remainingChannel = new HashMap<>(reactivePowerNegChannels); + remainingChannel.remove(i); + double reductionPercentage = reactivepowerDelta + / (reducedReactivePower[i] - reactivePowerSum / 3.0); + for (int j : remainingChannel.keySet()) { + reactivepowerDelta += (reducedReactivePower[j] - reactivePowerSum / 3) + * reductionPercentage; + } + } else if (reactivePowerPosChannels.containsKey(i) && reactivePowerPosChannels.size() > 1) { + Map> remainingChannel = new HashMap<>(reactivePowerPosChannels); + remainingChannel.remove(i); + double reductionPercentage = reactivepowerDelta + / (reducedReactivePower[i] - reactivePowerSum / 3.0); + for (int j : remainingChannel.keySet()) { + reactivepowerDelta += (reducedReactivePower[j] - reactivePowerSum / 3.0) + * reductionPercentage; + } } + reactivePowerReduction = Math.max(reactivepowerDelta, reactivePowerReduction); + } + if (activePowerReduction > activePowerAsymmetriePosSum) { + activePowerReduction = activePowerAsymmetriePosSum; + } + if (reactivePowerReduction > reactivePowerAsymmetriePosSum) { + reactivePowerReduction = reactivePowerAsymmetriePosSum; + } + for (int i = 0; i < 3; i++) { + long delta = (long) ((double) activePowerReduction / (double) activePowerAsymmetriePosSum + * (reducedActivePower[i] - activePowerSum / 3.0)); + reducedActivePower[i] -= delta; + } + for (int i = 0; i < 3; i++) { + long delta = (long) ((double) reactivePowerReduction / (double) reactivePowerAsymmetriePosSum + * (reducedReactivePower[i] - reactivePowerSum / 3.0)); + reducedReactivePower[i] -= delta; } } + break; default: case PERPHASE: - - break; - } - // reduce to min/max values - for (int i = 0; i < 3; i++) { - long minReactivePower = Collections.max(minReactivePowerPhase[i]); - long maxReactivePower = Collections.min(maxReactivePowerPhase[i]); - long minActivePower = Collections.max(minActivePowerPhase[i]); - long maxActivePower = Collections.min(maxActivePowerPhase[i]); - if (activePower[i] > maxActivePower) { - reducedActivePower[i] = maxActivePower; - } else if (activePower[i] < minActivePower) { - reducedActivePower[i] = minActivePower; - } - if (reactivePower[i] > maxReactivePower) { - reducedReactivePower[i] = maxReactivePower; - } else if (reactivePower[i] < minReactivePower) { - reducedReactivePower[i] = minReactivePower; + // reduce to min/max values + for (int i = 0; i < 3; i++) { + if (reducedActivePower[i] < 0) { + minActivePowerPhase[i].add(allowedCharge.value() / activePowerNegSum * reducedActivePower[i]); + } else { + maxActivePowerPhase[i].add(allowedDischarge.value() / activePowerPosSum * reducedActivePower[i]); + } + long minReactivePower = Collections.max(minReactivePowerPhase[i]); + long maxReactivePower = Collections.min(maxReactivePowerPhase[i]); + long minActivePower = Collections.max(minActivePowerPhase[i]); + long maxActivePower = Collections.min(maxActivePowerPhase[i]); + if (activePower[i] > maxActivePower) { + reducedActivePower[i] = maxActivePower; + } else if (activePower[i] < minActivePower) { + reducedActivePower[i] = minActivePower; + } + if (reactivePower[i] > maxReactivePower) { + reducedReactivePower[i] = maxReactivePower; + } else if (reactivePower[i] < minReactivePower) { + reducedReactivePower[i] = minReactivePower; + } } + break; } - } catch ( - - InvalidValueException e) { + } catch (InvalidValueException e) { log.error("Failed to reduce power", e); } log.info( "Reduce activePower L1:[{}]->[{}], L2:[{}]->[{}],L3:[{}]->[{}] " + "and reactivePower L1:[{}]->[{}], L2:[{}]->[{}], L3:[{}]->[{}]", - new Object[] { activePower[0], reducedActivePower[0], activePower[1], reducedActivePower[1], - activePower[2], reducedActivePower[2], reactivePower[0], reducedReactivePower[0], - reactivePower[1], reducedReactivePower[1], reactivePower[2], reducedReactivePower[2] }); + new Object[] { activePower[0], reducedActivePower[0], activePower[1], reducedActivePower[1], + activePower[2], reducedActivePower[2], reactivePower[0], reducedReactivePower[0], + reactivePower[1], reducedReactivePower[1], reactivePower[2], reducedReactivePower[2] }); for (int i = 0; i < 3; i++) { this.activePower[i] = reducedActivePower[i]; this.reactivePower[i] = reducedReactivePower[i]; @@ -277,20 +359,4 @@ public void writePower(ReductionType reductionType) throws WriteChannelException reactivePower[2] = 0; } - private void setMinMaxValues(long limit, long power, List[] maxLimits, List[] minLimits, Long powerSum, - int phase) { - long diff = limit - power; - for (int i = 0; i < 3; i++) { - long delta = Math.abs(diff); - if (i != phase) { - delta /= 2; - } - if (this.activePower[i] > powerSum) { - maxLimits[i].add(this.activePower[i] - delta); - } else { - minLimits[i].add(this.activePower[i] + delta); - } - } - } - } From 2d501453209b4c04a54d838ec24764eeb77fa6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Mar 2018 09:37:35 +0100 Subject: [PATCH 145/156] implement new SymmetricPower --- ...ymmetricSymmetricCombinationEssNature.java | 631 ++++-------------- 1 file changed, 131 insertions(+), 500 deletions(-) diff --git a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java index e77b3d46363..cfd9a836032 100644 --- a/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java +++ b/edge/src/io/openems/impl/device/system/asymmetricsymmetriccombinationess/AsymmetricSymmetricCombinationEssNature.java @@ -1,7 +1,5 @@ package io.openems.impl.device.system.asymmetricsymmetriccombinationess; -import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -31,8 +29,11 @@ import io.openems.core.BridgeInitializedEventListener; import io.openems.core.Config; import io.openems.core.ThingRepository; +import io.openems.core.utilities.AsymmetricPower; +import io.openems.core.utilities.AsymmetricPower.ReductionType; import io.openems.core.utilities.ControllerUtils; import io.openems.core.utilities.power.symmetric.SymmetricPower; +import io.openems.core.utilities.power.symmetric.SymmetricPowerProxy; import io.openems.impl.protocol.system.SystemDeviceNature; @ThingInfo(title = "Ess Asymmetric-Symmetric-Combination") @@ -58,6 +59,9 @@ public class AsymmetricSymmetricCombinationEssNature extends SystemDeviceNature private ProxyReadChannel systemState = new ProxyReadChannel("SystemState", this); private ProxyReadChannel capacity = new ProxyReadChannel("Capacity", this); private ProxyReadChannel maxNominalPower = new ProxyReadChannel("MaxNominalPower", this); + private SymmetricPowerProxy power; + private AsymmetricPower nativeAsymmetricPower; + private FunctionalReadChannel activePowerL1 = new FunctionalReadChannel("ActivePowerL1", this, new FunctionalReadChannelFunction() { @@ -236,15 +240,29 @@ public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channel @Override public Long getMinValue(Optional minValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMinPowerPhase(1, minValue, getNativeSetPower(1), getThisSetPower(1), - getThisSetReactivePower(1), setActivePower, setReactivePower); + Long min = null; + if(minValue.isPresent() && essNature != null && essNature.setActivePowerL1().writeMin().isPresent()) { + min = Math.max(minValue.get(), essNature.setActivePowerL1().writeMin().get()); + }else if(minValue.isPresent()) { + min = minValue.get(); + }else if(essNature.setActivePowerL1().writeMin().isPresent()) { + min = essNature.setActivePowerL1().writeMin().get(); + } + return min; } @Override public Long getMaxValue(Optional maxValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMaxPowerPhase(1, maxValue, getNativeSetPower(1), getThisSetPower(1), - getThisSetReactivePower(1), setActivePower, setReactivePower); + Long max = null; + if(maxValue.isPresent() && essNature != null && essNature.setActivePowerL1().writeMax().isPresent()) { + max = Math.min(maxValue.get(), essNature.setActivePowerL1().writeMax().get()); + }else if(maxValue.isPresent()) { + max = maxValue.get(); + }else if(essNature.setActivePowerL1().writeMax().isPresent()) { + max = essNature.setActivePowerL1().writeMax().get(); + } + return max; } @Override @@ -299,15 +317,29 @@ public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channel @Override public Long getMinValue(Optional minValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMinPowerPhase(2, minValue, getNativeSetPower(2), getThisSetPower(2), - getThisSetReactivePower(2), setActivePower, setReactivePower); + Long min = null; + if(minValue.isPresent() && essNature != null && essNature.setActivePowerL2().writeMin().isPresent()) { + min = Math.max(minValue.get(), essNature.setActivePowerL2().writeMin().get()); + }else if(minValue.isPresent()) { + min = minValue.get(); + }else if(essNature.setActivePowerL2().writeMin().isPresent()) { + min = essNature.setActivePowerL2().writeMin().get(); + } + return min; } @Override public Long getMaxValue(Optional maxValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMaxPowerPhase(2, maxValue, getNativeSetPower(2), getThisSetPower(2), - getThisSetReactivePower(2), setActivePower, setReactivePower); + Long max = null; + if(maxValue.isPresent() && essNature != null && essNature.setActivePowerL2().writeMax().isPresent()) { + max = Math.min(maxValue.get(), essNature.setActivePowerL2().writeMax().get()); + }else if(maxValue.isPresent()) { + max = maxValue.get(); + }else if(essNature.setActivePowerL2().writeMax().isPresent()) { + max = essNature.setActivePowerL2().writeMax().get(); + } + return max; } @Override @@ -362,15 +394,29 @@ public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channel @Override public Long getMinValue(Optional minValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMinPowerPhase(3, minValue, getNativeSetPower(3), getThisSetPower(3), - getThisSetReactivePower(3), setActivePower, setReactivePower); + Long min = null; + if(minValue.isPresent() && essNature != null && essNature.setActivePowerL3().writeMin().isPresent()) { + min = Math.max(minValue.get(), essNature.setActivePowerL3().writeMin().get()); + }else if(minValue.isPresent()) { + min = minValue.get(); + }else if(essNature.setActivePowerL3().writeMin().isPresent()) { + min = essNature.setActivePowerL3().writeMin().get(); + } + return min; } @Override public Long getMaxValue(Optional maxValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMaxPowerPhase(3, maxValue, getNativeSetPower(3), getThisSetPower(3), - getThisSetReactivePower(3), setActivePower, setReactivePower); + Long max = null; + if(maxValue.isPresent() && essNature != null && essNature.setActivePowerL3().writeMax().isPresent()) { + max = Math.min(maxValue.get(), essNature.setActivePowerL3().writeMax().get()); + }else if(maxValue.isPresent()) { + max = maxValue.get(); + }else if(essNature.setActivePowerL3().writeMax().isPresent()) { + max = essNature.setActivePowerL3().writeMax().get(); + } + return max; } @Override @@ -397,96 +443,6 @@ public Long setMaxValue(Long newValue, String newLabel, }); - private FunctionalWriteChannel setActivePower = new FunctionalWriteChannel("SetActivePower", this, - new FunctionalWriteChannelFunction() { - - @Override - public Long setValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - if (channels.length == 3) { - for (int i = 0; i < 3; i++) { - channels[i].checkIntervalBoundaries(newValue / 3); - } - return newValue; - } else { - throw new WriteChannelException("no essNature to control available"); - } - } - - @Override - public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channels) { - Long sum = 0L; - for (ReadChannel channel : channels) { - if (channel == null || !channel.valueOptional().isPresent()) { - return null; - } - sum += channel.valueOptional().get(); - } - return sum; - } - - @SuppressWarnings("unchecked") - @Override - public Long getMinValue(Optional minValue, WriteChannel... channels) { - WriteChannel[] nativeSetPrimaryPowerPhase = new WriteChannel[3]; - WriteChannel[] thisSetPrimaryPowerPhase = new WriteChannel[] { setActivePowerL1, - setActivePowerL2, setActivePowerL3 }; - WriteChannel[] thisSetSecundaryPowerPhase = new WriteChannel[] { setReactivePowerL1, - setReactivePowerL2, setReactivePowerL3 }; - if (channels.length == 3) { - nativeSetPrimaryPowerPhase = channels; - } - Long minActivePower = getMinPower(nativeSetPrimaryPowerPhase, thisSetPrimaryPowerPhase, - thisSetSecundaryPowerPhase, setActivePower, setReactivePower); - return minActivePower; - } - - @SuppressWarnings("unchecked") - @Override - public Long getMaxValue(Optional maxValue, WriteChannel... channels) { - WriteChannel[] nativeSetPrimaryPowerPhase = new WriteChannel[3]; - WriteChannel[] thisSetPrimaryPowerPhase = new WriteChannel[] { setActivePowerL1, - setActivePowerL2, setActivePowerL3 }; - WriteChannel[] thisSetSecundaryPowerPhase = new WriteChannel[] { setReactivePowerL1, - setReactivePowerL2, setReactivePowerL3 }; - if (channels.length == 3) { - nativeSetPrimaryPowerPhase = channels; - } - Long maxActivePower = getMaxPower(nativeSetPrimaryPowerPhase, thisSetPrimaryPowerPhase, - thisSetSecundaryPowerPhase, setActivePower, setReactivePower); - if (maxActivePower == null) { - return null; - } - return maxActivePower; - } - - @Override - public Long setMinValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - if (channels.length == 3) { - for (int i = 0; i < 3; i++) { - channels[i].checkIntervalBoundaries(newValue / 3); - } - return newValue; - } else { - throw new WriteChannelException("no essNature to control available"); - } - } - - @Override - public Long setMaxValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - if (channels.length == 3) { - for (int i = 0; i < 3; i++) { - channels[i].checkIntervalBoundaries(newValue / 3); - } - return newValue; - } else { - throw new WriteChannelException("no essNature to control available"); - } - } - - }); private FunctionalWriteChannel setReactivePowerL1 = new FunctionalWriteChannel("SetReactivePowerL1", this, new FunctionalWriteChannelFunction() { @@ -516,15 +472,29 @@ public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channel @Override public Long getMinValue(Optional minValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMinPowerPhase(1, minValue, getNativeSetReactivePower(1), getThisSetReactivePower(1), - getThisSetPower(1), setReactivePower, setActivePower); + Long min = null; + if(minValue.isPresent() && essNature != null && essNature.setReactivePowerL1().writeMin().isPresent()) { + min = Math.max(minValue.get(), essNature.setReactivePowerL1().writeMin().get()); + }else if(minValue.isPresent()) { + min = minValue.get(); + }else if(essNature.setReactivePowerL1().writeMin().isPresent()) { + min = essNature.setReactivePowerL1().writeMin().get(); + } + return min; } @Override public Long getMaxValue(Optional maxValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMaxPowerPhase(1, maxValue, getNativeSetReactivePower(1), getThisSetReactivePower(1), - getThisSetPower(1), setReactivePower, setActivePower); + Long max = null; + if(maxValue.isPresent() && essNature != null && essNature.setReactivePowerL1().writeMax().isPresent()) { + max = Math.min(maxValue.get(), essNature.setReactivePowerL1().writeMax().get()); + }else if(maxValue.isPresent()) { + max = maxValue.get(); + }else if(essNature.setReactivePowerL1().writeMax().isPresent()) { + max = essNature.setReactivePowerL1().writeMax().get(); + } + return max; } @Override @@ -579,15 +549,29 @@ public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channel @Override public Long getMinValue(Optional minValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMinPowerPhase(2, minValue, getNativeSetReactivePower(2), getThisSetReactivePower(2), - getThisSetPower(2), setReactivePower, setActivePower); + Long min = null; + if(minValue.isPresent() && essNature != null && essNature.setReactivePowerL2().writeMin().isPresent()) { + min = Math.max(minValue.get(), essNature.setReactivePowerL2().writeMin().get()); + }else if(minValue.isPresent()) { + min = minValue.get(); + }else if(essNature.setReactivePowerL2().writeMin().isPresent()) { + min = essNature.setReactivePowerL2().writeMin().get(); + } + return min; } @Override public Long getMaxValue(Optional maxValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMaxPowerPhase(2, maxValue, getNativeSetReactivePower(2), getThisSetReactivePower(2), - getThisSetPower(2), setReactivePower, setActivePower); + Long max = null; + if(maxValue.isPresent() && essNature != null && essNature.setReactivePowerL2().writeMax().isPresent()) { + max = Math.min(maxValue.get(), essNature.setReactivePowerL2().writeMax().get()); + }else if(maxValue.isPresent()) { + max = maxValue.get(); + }else if(essNature.setReactivePowerL2().writeMax().isPresent()) { + max = essNature.setReactivePowerL2().writeMax().get(); + } + return max; } @Override @@ -642,15 +626,29 @@ public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channel @Override public Long getMinValue(Optional minValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMinPowerPhase(3, minValue, getNativeSetReactivePower(3), getThisSetReactivePower(3), - getThisSetPower(3), setReactivePower, setActivePower); + Long min = null; + if(minValue.isPresent() && essNature != null && essNature.setReactivePowerL3().writeMin().isPresent()) { + min = Math.max(minValue.get(), essNature.setReactivePowerL3().writeMin().get()); + }else if(minValue.isPresent()) { + min = minValue.get(); + }else if(essNature.setReactivePowerL3().writeMin().isPresent()) { + min = essNature.setReactivePowerL3().writeMin().get(); + } + return min; } @Override public Long getMaxValue(Optional maxValue, @SuppressWarnings("unchecked") WriteChannel... channels) { - return getMaxPowerPhase(3, maxValue, getNativeSetReactivePower(3), getThisSetReactivePower(3), - getThisSetPower(3), setReactivePower, setActivePower); + Long max = null; + if(maxValue.isPresent() && essNature != null && essNature.setReactivePowerL3().writeMax().isPresent()) { + max = Math.min(maxValue.get(), essNature.setReactivePowerL3().writeMax().get()); + }else if(maxValue.isPresent()) { + max = maxValue.get(); + }else if(essNature.setReactivePowerL3().writeMax().isPresent()) { + max = essNature.setReactivePowerL3().writeMax().get(); + } + return max; } @Override @@ -677,99 +675,6 @@ public Long setMaxValue(Long newValue, String newLabel, }); - private FunctionalWriteChannel setReactivePower = new FunctionalWriteChannel("SetReactivePower", this, - new FunctionalWriteChannelFunction() { - - @Override - public Long setValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - if (channels.length == 3) { - for (int i = 0; i < 3; i++) { - channels[i].checkIntervalBoundaries(newValue / 3); - } - return newValue; - } else { - throw new WriteChannelException("no essNature to control available"); - } - } - - @Override - public Long getValue(@SuppressWarnings("unchecked") ReadChannel... channels) { - Long sum = 0L; - for (ReadChannel channel : channels) { - if (channel == null || !channel.valueOptional().isPresent()) { - return null; - } - sum += channel.valueOptional().get(); - } - return sum; - } - - @SuppressWarnings("unchecked") - @Override - public Long getMinValue(Optional minValue, WriteChannel... channels) { - WriteChannel[] nativeSetPrimaryPowerPhase = new WriteChannel[3]; - WriteChannel[] thisSetSecundaryPowerPhase = new WriteChannel[] { setActivePowerL1, - setActivePowerL2, setActivePowerL3 }; - WriteChannel[] thisSetPrimaryPowerPhase = new WriteChannel[] { setReactivePowerL1, - setReactivePowerL2, setReactivePowerL3 }; - if (channels.length == 3) { - nativeSetPrimaryPowerPhase = channels; - } - Long minReactivePower = getMinPower(nativeSetPrimaryPowerPhase, thisSetPrimaryPowerPhase, - thisSetSecundaryPowerPhase, setReactivePower, setActivePower); - if (minReactivePower == null) { - return null; - } - return minReactivePower; - } - - @SuppressWarnings("unchecked") - @Override - public Long getMaxValue(Optional maxValue, WriteChannel... channels) { - WriteChannel[] nativeSetPrimaryPowerPhase = new WriteChannel[3]; - WriteChannel[] thisSetSecundaryPowerPhase = new WriteChannel[] { setActivePowerL1, - setActivePowerL2, setActivePowerL3 }; - WriteChannel[] thisSetPrimaryPowerPhase = new WriteChannel[] { setReactivePowerL1, - setReactivePowerL2, setReactivePowerL3 }; - if (channels.length == 3) { - nativeSetPrimaryPowerPhase = channels; - } - Long maxReactivePower = getMaxPower(nativeSetPrimaryPowerPhase, thisSetPrimaryPowerPhase, - thisSetSecundaryPowerPhase, setReactivePower, setActivePower); - if (maxReactivePower == null) { - return null; - } - return maxReactivePower; - } - - @Override - public Long setMinValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - if (channels.length == 3) { - for (int i = 0; i < 3; i++) { - channels[i].checkIntervalBoundaries(newValue / 3); - } - return newValue; - } else { - throw new WriteChannelException("no essNature to control available"); - } - } - - @Override - public Long setMaxValue(Long newValue, String newLabel, - @SuppressWarnings("unchecked") WriteChannel... channels) throws WriteChannelException { - if (channels.length == 3) { - for (int i = 0; i < 3; i++) { - channels[i].checkIntervalBoundaries(newValue / 3); - } - return newValue; - } else { - throw new WriteChannelException("no essNature to control available"); - } - } - - }); private FunctionalWriteChannel setWorkState = new FunctionalWriteChannel("SetWorkState", this, new FunctionalWriteChannelFunction() { @@ -1056,9 +961,6 @@ private void loadEss() { setActivePowerL3.removeChannel(essNature.setActivePowerL1()); setActivePowerL3.removeChannel(essNature.setActivePowerL2()); setActivePowerL3.removeChannel(essNature.setActivePowerL3()); - setActivePower.removeChannel(essNature.setActivePowerL1()); - setActivePower.removeChannel(essNature.setActivePowerL2()); - setActivePower.removeChannel(essNature.setActivePowerL3()); setReactivePowerL1.removeChannel(essNature.setReactivePowerL1()); setReactivePowerL1.removeChannel(essNature.setReactivePowerL2()); setReactivePowerL1.removeChannel(essNature.setReactivePowerL3()); @@ -1068,10 +970,9 @@ private void loadEss() { setReactivePowerL3.removeChannel(essNature.setReactivePowerL1()); setReactivePowerL3.removeChannel(essNature.setReactivePowerL2()); setReactivePowerL3.removeChannel(essNature.setReactivePowerL3()); - setReactivePower.removeChannel(essNature.setReactivePowerL1()); - setReactivePower.removeChannel(essNature.setReactivePowerL2()); - setReactivePower.removeChannel(essNature.setReactivePowerL3()); setWorkState.removeChannel(essNature.setWorkState()); + power = null; + nativeAsymmetricPower = null; } essNature = null; if (essId != null) { @@ -1123,9 +1024,6 @@ private void loadEss() { setActivePowerL3.addChannel(essNature.setActivePowerL1()); setActivePowerL3.addChannel(essNature.setActivePowerL2()); setActivePowerL3.addChannel(essNature.setActivePowerL3()); - setActivePower.addChannel(essNature.setActivePowerL1()); - setActivePower.addChannel(essNature.setActivePowerL2()); - setActivePower.addChannel(essNature.setActivePowerL3()); setReactivePowerL1.addChannel(essNature.setReactivePowerL1()); setReactivePowerL1.addChannel(essNature.setReactivePowerL2()); setReactivePowerL1.addChannel(essNature.setReactivePowerL3()); @@ -1135,10 +1033,12 @@ private void loadEss() { setReactivePowerL3.addChannel(essNature.setReactivePowerL1()); setReactivePowerL3.addChannel(essNature.setReactivePowerL2()); setReactivePowerL3.addChannel(essNature.setReactivePowerL3()); - setReactivePower.addChannel(essNature.setReactivePowerL1()); - setReactivePower.addChannel(essNature.setReactivePowerL2()); - setReactivePower.addChannel(essNature.setReactivePowerL3()); setWorkState.addChannel(essNature.setWorkState()); + power = new SymmetricPowerProxy(essNature.maxNominalPower().value(), getParent().getBridge()); + nativeAsymmetricPower = new AsymmetricPower(essNature.allowedDischarge().required(), essNature.allowedCharge().required(), + essNature.allowedApparent().required(), essNature.setActivePowerL1().required(), essNature.setActivePowerL2().required(), + essNature.setActivePowerL3().required(), essNature.setReactivePowerL1().required(), + essNature.setReactivePowerL2().required(), essNature.setReactivePowerL3().required()); } } else { log.error("ThingID: " + essId + " is no AsymmetricEss!"); @@ -1156,8 +1056,8 @@ public void runCalculation() throws WriteChannelException { long activePowerL1 = 0; long activePowerL2 = 0; long activePowerL3 = 0; - if (setActivePower.getWriteValue().isPresent()) { - long activePowerWriteValue = setActivePower.getWriteValue().get(); + if (power != null && power.getActivePowerSetPoint().isPresent()) { + long activePowerWriteValue = power.getActivePowerSetPoint().get(); activePowerL1 += activePowerWriteValue / 3; activePowerL2 += activePowerWriteValue / 3; activePowerL3 += activePowerWriteValue / 3; @@ -1168,293 +1068,25 @@ public void runCalculation() throws WriteChannelException { activePowerL2 += setActivePowerL2.getWriteValue().get(); activePowerL3 += setActivePowerL3.getWriteValue().get(); } - essNature.setActivePowerL1().pushWrite(activePowerL1); - essNature.setActivePowerL2().pushWrite(activePowerL2); - essNature.setActivePowerL3().pushWrite(activePowerL3); long reactivePowerL1 = 0; long reactivePowerL2 = 0; long reactivePowerL3 = 0; - if (setReactivePower.getWriteValue().isPresent()) { - long reactivePowerWriteValue = setReactivePower.getWriteValue().get(); + if (power != null && power.getReactivePowerSetPoint().isPresent()) { + long reactivePowerWriteValue = power.getReactivePowerSetPoint().get(); reactivePowerL1 += reactivePowerWriteValue / 3; reactivePowerL2 += reactivePowerWriteValue / 3; reactivePowerL3 += reactivePowerWriteValue / 3; } - if (setReactivePowerL1.getWriteValue().isPresent()) { + if (setReactivePowerL1.getWriteValue().isPresent() && setReactivePowerL2.getWriteValue().isPresent() && setReactivePowerL3.getWriteValue().isPresent()) { reactivePowerL1 += setReactivePowerL1.getWriteValue().get(); - } - if (setReactivePowerL2.getWriteValue().isPresent()) { reactivePowerL2 += setReactivePowerL2.getWriteValue().get(); - } - if (setReactivePowerL3.getWriteValue().isPresent()) { reactivePowerL3 += setReactivePowerL3.getWriteValue().get(); } - essNature.setReactivePowerL1().pushWrite(reactivePowerL1); - essNature.setReactivePowerL2().pushWrite(reactivePowerL2); - essNature.setReactivePowerL3().pushWrite(reactivePowerL3); - } - - private Long getMinPowerPhase(int phase, Optional thisMinPower, WriteChannel nativeSetPrimaryPowerPhase, - WriteChannel thisSetPrimaryPowerPhase, WriteChannel thisSetSecundaryPowerPhase, - WriteChannel thisSetPrimaryPower, WriteChannel thisSetSecundaryPower) { - Long minValue = null; - if (nativeSetPrimaryPowerPhase != null && nativeSetPrimaryPowerPhase.getWriteValue().isPresent()) { - minValue = 0L; - } else if (thisSetPrimaryPowerPhase.getWriteValue().isPresent()) { - minValue = thisSetPrimaryPowerPhase.getWriteValue().get(); - } else { - List minValues = new ArrayList<>(); - if (thisMinPower.isPresent()) { - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - minValues.add(thisMinPower.get() - thisSetPrimaryPower.getWriteValue().get() / 3); - } else { - minValues.add(thisMinPower.get()); - } - } - if (allowedApparent.valueOptional().isPresent()) { - long primaryPowerSum = 0L; - long scundaryPowerSum = 0L; - if (thisSetPrimaryPowerPhase.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPowerPhase.getWriteValue().isPresent()) { - scundaryPowerSum += thisSetSecundaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPower.getWriteValue().isPresent()) { - scundaryPowerSum += thisSetSecundaryPower.getWriteValue().get() / 3; - } - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPower.getWriteValue().get() / 3; - } - long allowedPrimaryPowerPhase = ControllerUtils.calculateActivePower(scundaryPowerSum, - allowedApparent.valueOptional().get() / 3); - if (primaryPowerSum > 0) { - minValues.add(allowedPrimaryPowerPhase * -1); - } else { - minValues.add(allowedPrimaryPowerPhase * -1 - primaryPowerSum); - } - } - if (nativeSetPrimaryPowerPhase != null && nativeSetPrimaryPowerPhase.writeMin().isPresent()) { - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - minValues.add(nativeSetPrimaryPowerPhase.writeMin().get() - - thisSetPrimaryPower.getWriteValue().get() / 3); - } else { - minValues.add(nativeSetPrimaryPowerPhase.writeMin().get()); - } - } - if (minValues.size() > 0) { - minValue = Collections.max(minValues); - } - } - return minValue; - - } - - private Long getMaxPowerPhase(int phase, Optional thisMaxPower, WriteChannel nativeSetPrimaryPowerPhase, - WriteChannel thisSetPrimaryPowerPhase, WriteChannel thisSetSecundaryPowerPhase, - WriteChannel thisSetPrimaryPower, WriteChannel thisSetSecundaryPower) { - Long maxValue = null; - if (nativeSetPrimaryPowerPhase != null && nativeSetPrimaryPowerPhase.getWriteValue().isPresent()) { - maxValue = 0L; - } else if (thisSetPrimaryPowerPhase.getWriteValue().isPresent()) { - maxValue = thisSetPrimaryPowerPhase.getWriteValue().get(); - } else { - List maxValues = new ArrayList<>(); - if (thisMaxPower.isPresent()) { - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - maxValues.add(thisMaxPower.get() - thisSetPrimaryPower.getWriteValue().get() / 3); - } else { - maxValues.add(thisMaxPower.get()); - } - } - if (allowedApparent.valueOptional().isPresent()) { - long primaryPowerSum = 0L; - long secundaryPowerSum = 0L; - if (thisSetPrimaryPowerPhase.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPowerPhase.getWriteValue().isPresent()) { - secundaryPowerSum += thisSetSecundaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPower.getWriteValue().isPresent()) { - secundaryPowerSum += thisSetSecundaryPower.getWriteValue().get() / 3; - } - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPower.getWriteValue().get() / 3; - } - long allowedPowerPhase = ControllerUtils.calculateActivePower(secundaryPowerSum, - allowedApparent.valueOptional().get() / 3); - if (primaryPowerSum > 0) { - maxValues.add(allowedPowerPhase - primaryPowerSum); - } else { - maxValues.add(allowedPowerPhase); - } - } - if (nativeSetPrimaryPowerPhase != null && nativeSetPrimaryPowerPhase.writeMax().isPresent()) { - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - maxValues.add(nativeSetPrimaryPowerPhase.writeMax().get() - - thisSetPrimaryPower.getWriteValue().get() / 3); - } else { - maxValues.add(nativeSetPrimaryPowerPhase.writeMax().get()); - } - } - if (maxValues.size() > 0) { - maxValue = Collections.min(maxValues); - } - } - return maxValue; - } - - private WriteChannel getNativeSetPower(int phase) { - if (essNature != null) { - switch (phase) { - case 1: - return essNature.setActivePowerL1(); - case 2: - return essNature.setActivePowerL2(); - case 3: - return essNature.setActivePowerL3(); - } - return null; - } else { - return null; - } - } - - private WriteChannel getThisSetPower(int phase) { - switch (phase) { - case 1: - return setActivePowerL1; - case 2: - return setActivePowerL2; - case 3: - return setActivePowerL3; - } - return null; - } - - private Long getMinPower(WriteChannel[] nativeSetPrimaryPowerPhases, - WriteChannel[] thisSetPrimaryPowerPhases, WriteChannel[] thisSetSecundaryPowerPhases, - WriteChannel thisSetPrimaryPower, WriteChannel thisSetSecundaryPower) { - Long minValue = null; - for (WriteChannel nativeSetPower : nativeSetPrimaryPowerPhases) { - if (nativeSetPower != null && nativeSetPower.getWriteValue().isPresent()) { - return 0L; - } - } - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - minValue = thisSetPrimaryPower.getWriteValue().get(); - } else { - List minValues = new ArrayList<>(); - if (essNature != null && essNature.allowedApparent().valueOptional().isPresent()) { - for (int i = 0; i < 3; i++) { - WriteChannel thisSetPrimaryPowerPhase = thisSetPrimaryPowerPhases[i]; - WriteChannel thisSetSecundarzPowerPhase = thisSetSecundaryPowerPhases[i]; - long primaryPowerSum = 0L; - long secundaryPowerSum = 0L; - if (thisSetPrimaryPowerPhase.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundarzPowerPhase.getWriteValue().isPresent()) { - secundaryPowerSum += thisSetSecundarzPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPower.getWriteValue().isPresent()) { - secundaryPowerSum += thisSetSecundaryPower.getWriteValue().get() / 3; - } - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPower.getWriteValue().get() / 3; - } - long allowedPrimaryPowerPhase = ControllerUtils.calculateActivePower(secundaryPowerSum, - allowedApparent.valueOptional().get() / 3); - if (primaryPowerSum > 0) { - minValues.add(allowedPrimaryPowerPhase * -1); - } else { - minValues.add(allowedPrimaryPowerPhase * -1 - primaryPowerSum); - } - if (nativeSetPrimaryPowerPhases[i].writeMin().isPresent()) { - minValues.add(nativeSetPrimaryPowerPhases[i].writeMin().get()); - } - } - minValue = Collections.max(minValues) * 3; - } - } - return minValue; - } - - private Long getMaxPower(WriteChannel[] nativeSetPrimaryPowerPhases, - WriteChannel[] thisSetPrimaryPowerPhases, WriteChannel[] thisSetSecundaryPowerPhases, - WriteChannel thisSetPrimaryPower, WriteChannel thisSetSecundaryPower) { - Long maxValue = null; - for (WriteChannel nativeSetPower : nativeSetPrimaryPowerPhases) { - if (nativeSetPower != null && nativeSetPower.getWriteValue().isPresent()) { - return 0L; - } - } - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - maxValue = thisSetPrimaryPower.getWriteValue().get(); - } else { - List maxValues = new ArrayList<>(); - if (essNature != null && essNature.allowedApparent().valueOptional().isPresent()) { - for (int i = 0; i < 3; i++) { - WriteChannel thisSetPrimaryPowerPhase = thisSetPrimaryPowerPhases[i]; - WriteChannel thisSetSecundaryPowerPhase = thisSetSecundaryPowerPhases[i]; - long primaryPowerSum = 0L; - long secundaryPowerSum = 0L; - if (thisSetPrimaryPowerPhase.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPowerPhase.getWriteValue().isPresent()) { - secundaryPowerSum += thisSetSecundaryPowerPhase.getWriteValue().get(); - } - if (thisSetSecundaryPower.getWriteValue().isPresent()) { - secundaryPowerSum += thisSetSecundaryPower.getWriteValue().get() / 3; - } - if (thisSetPrimaryPower.getWriteValue().isPresent()) { - primaryPowerSum += thisSetPrimaryPower.getWriteValue().get() / 3; - } - long allowedPrimaryPowerPhase = ControllerUtils.calculateActivePower(secundaryPowerSum, - allowedApparent.valueOptional().get() / 3); - if (primaryPowerSum > 0) { - maxValues.add(allowedPrimaryPowerPhase - primaryPowerSum); - } else { - maxValues.add(allowedPrimaryPowerPhase); - } - if (nativeSetPrimaryPowerPhases[i].writeMax().isPresent()) { - maxValues.add(nativeSetPrimaryPowerPhases[i].writeMax().get()); - } - } - maxValue = Collections.min(maxValues) * 3; - } - } - return maxValue; - } - - private WriteChannel getNativeSetReactivePower(int phase) { - if (essNature != null) { - switch (phase) { - case 1: - return essNature.setReactivePowerL1(); - case 2: - return essNature.setReactivePowerL2(); - case 3: - return essNature.setReactivePowerL3(); - } - return null; - } else { - return null; - } - } - - private WriteChannel getThisSetReactivePower(int phase) { - switch (phase) { - case 1: - return setReactivePowerL1; - case 2: - return setReactivePowerL2; - case 3: - return setReactivePowerL3; + if(nativeAsymmetricPower != null) { + nativeAsymmetricPower.setActivePower(activePowerL1, activePowerL2, activePowerL3); + nativeAsymmetricPower.setReactivePower(reactivePowerL1, reactivePowerL2, reactivePowerL3); + nativeAsymmetricPower.writePower(ReductionType.PERSUM); } - return null; } @Override @@ -1472,8 +1104,7 @@ public void onBridgeInitialized() { @Override public SymmetricPower getPower() { - // TODO Auto-generated method stub - return null; + return power; } @Override From fb3c7e7a0f59cc5780ede15a3288aa95c1672bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ro=C3=9Fmann?= Date: Tue, 13 Mar 2018 09:38:30 +0100 Subject: [PATCH 146/156] add SymmetricPowerProxy --- .../power/symmetric/SymmetricPowerProxy.java | 142 + ui/yarn.lock | 2921 ++++++++++++++--- 2 files changed, 2535 insertions(+), 528 deletions(-) create mode 100644 edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerProxy.java diff --git a/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerProxy.java b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerProxy.java new file mode 100644 index 00000000000..633f492d378 --- /dev/null +++ b/edge/src/io/openems/core/utilities/power/symmetric/SymmetricPowerProxy.java @@ -0,0 +1,142 @@ +package io.openems.core.utilities.power.symmetric; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.Point; + +import io.openems.api.bridge.Bridge; +import io.openems.api.bridge.BridgeEvent; +import io.openems.api.bridge.BridgeEventListener; + +public class SymmetricPowerProxy extends SymmetricPower implements LimitationChangedListener, BridgeEventListener { + /* + * Object + */ + + private Geometry baseGeometry; + + private List staticLimitations; + private List dynamicLimitations; + private double lastActivePower = 0; + private double lastReactivePower = 0; + private Optional activePower; + private Optional reactivePower; + + public SymmetricPowerProxy(long maxApparentPower, Bridge bridge) { + setMaxApparentPower(maxApparentPower); + this.staticLimitations = new ArrayList<>(); + this.dynamicLimitations = new ArrayList<>(); + if (bridge != null) { + bridge.addListener(this); + } else { + log.error("the Bridge is null! the Power Values won't be writte!"); + } + createBaseGeometry(); + reset(); + } + + public void addStaticLimitation(Limitation limit) { + this.staticLimitations.add(limit); + limit.addListener(this); + createBaseGeometry(); + } + + public void removeStaticLimitation(Limitation limit) { + limit.removeListener(this); + this.staticLimitations.remove(limit); + createBaseGeometry(); + } + + @Override + protected void reset() { + this.dynamicLimitations.clear(); + this.setGeometry(baseGeometry); + activePower = Optional.empty(); + reactivePower = Optional.empty(); + super.reset(); + } + + @Override + public void applyLimitation(Limitation limit) throws PowerException { + Geometry limitedPower = limit.applyLimit(getGeometry()); + if (!limitedPower.isEmpty()) { + setGeometry(limitedPower); + this.dynamicLimitations.add(limit); + } else { + throw new PowerException("No possible Power after applying Limit. Limit is not applied!"); + } + } + + private void writePower() { + if (dynamicLimitations.size() > 0) { + Point p = reduceToZero(); + Coordinate c = p.getCoordinate(); + setGeometry(p); + double activePowerDelta = c.x - lastActivePower; + double reactivePowerDelta = c.y - lastReactivePower; + lastActivePower += activePowerDelta / 2; + lastReactivePower += reactivePowerDelta / 2; + this.activePower = Optional.of((long)lastActivePower); + this.reactivePower = Optional.of((long)lastReactivePower); + } + } + + public Optional getActivePowerSetPoint(){ + writePower(); + return activePower; + } + + public Optional getReactivePowerSetPoint(){ + writePower(); + return reactivePower; + } + + private void createBaseGeometry() { + SHAPEFACTORY.setCentre(ZERO); + SHAPEFACTORY.setSize(getMaxApparentPower() * 2); + SHAPEFACTORY.setNumPoints(32); + this.baseGeometry = SHAPEFACTORY.createCircle(); + for (Limitation limit : this.staticLimitations) { + try { + Geometry limitedPower = limit.applyLimit(this.baseGeometry); + if (!limitedPower.isEmpty()) { + this.baseGeometry = limitedPower; + } else { + log.error("Power is empty after applying Limit. " + limit.toString()); + } + } catch (PowerException e) { + log.error("Failed to limit Power!", e); + } + } + } + + @Override + public void onLimitationChange(Limitation sender) { + if (staticLimitations.contains(sender)) { + createBaseGeometry(); + } + } + + @Override + public void onBridgeChange(BridgeEvent event) { + switch (event.getPosition()) { + case BEFOREREADOTHER1: + break; + case BEFOREREADOTHER2: + break; + case BEFOREREADREQUIRED: + break; + case BEFOREWRITE: + this.reset(); + break; + default: + break; + + } + } + +} diff --git a/ui/yarn.lock b/ui/yarn.lock index 7b8f5ced82d..88e662e4966 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2,53 +2,54 @@ # yarn lockfile v1 -"@angular-devkit/build-optimizer@~0.0.35": - version "0.0.36" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.36.tgz#e816ee9be22dbb777724f0281acfa72cfff184b7" +"@angular-devkit/build-optimizer@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.42.tgz#402b0dda4883db91e2381c3ddc55888408a7894e" dependencies: loader-utils "^1.1.0" source-map "^0.5.6" - typescript "~2.6.1" + typescript "~2.6.2" webpack-sources "^1.0.1" -"@angular-devkit/core@0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.22.tgz#e90f46bf7ff47d260a767959267bc65ffee39ef1" +"@angular-devkit/core@0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.29.tgz#6fb319b45a62eff172318cbe256fdb24ef20af2b" dependencies: + ajv "~5.5.1" + chokidar "^1.7.0" + rxjs "^5.5.6" source-map "^0.5.6" -"@angular-devkit/schematics@~0.0.40": - version "0.0.42" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.42.tgz#34eea7136455545c8abd21edf94a36870a073fea" +"@angular-devkit/schematics@0.0.52": + version "0.0.52" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.52.tgz#cbd2f42778b50d6422a254ffaec05ad4ef3cb6c0" dependencies: - "@angular-devkit/core" "0.0.22" "@ngtools/json-schema" "^1.1.0" - "@schematics/schematics" "0.0.11" - minimist "^1.2.0" - rxjs "^5.5.2" + rxjs "^5.5.6" -"@angular/animations@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.0.1.tgz#a92b2b186a6e5a31a9f1584911dd6aa7e16c5de1" +"@angular/animations@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.2.8.tgz#31cdb49163881323fa5ded41e89683ec5e9ac8b5" dependencies: tslib "^1.7.1" -"@angular/cdk@^5.0.0": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-5.0.4.tgz#f76a268e404f41aff0e908b21e7de9a5dfc07150" +"@angular/cdk@^5.2.0": + version "5.2.4" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-5.2.4.tgz#c0a429a8710d8fedb157f546e21cb49d4335f7f7" dependencies: tslib "^1.7.1" -"@angular/cli@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.6.0.tgz#eba521f6a8e4c2db628300baddbc4da13ca96998" +"@angular/cli@1.6.7": + version "1.6.7" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.6.7.tgz#e2323753c144b5de6c699bbebee688105a394641" dependencies: - "@angular-devkit/build-optimizer" "~0.0.35" - "@angular-devkit/schematics" "~0.0.40" + "@angular-devkit/build-optimizer" "0.0.42" + "@angular-devkit/core" "0.0.29" + "@angular-devkit/schematics" "0.0.52" "@ngtools/json-schema" "1.1.0" - "@ngtools/webpack" "1.9.0" - "@schematics/angular" "~0.1.10" - autoprefixer "^6.5.3" + "@ngtools/webpack" "1.9.7" + "@schematics/angular" "0.1.17" + autoprefixer "^7.2.3" chalk "~2.2.0" circular-dependency-plugin "^4.2.1" common-tags "^1.3.1" @@ -64,11 +65,12 @@ fs-extra "^4.0.0" glob "^7.0.3" html-webpack-plugin "^2.29.0" - istanbul-instrumenter-loader "^2.0.0" + istanbul-instrumenter-loader "^3.0.0" karma-source-map-support "^1.2.0" less "^2.7.2" less-loader "^4.0.5" license-webpack-plugin "^1.0.0" + loader-utils "1.1.0" lodash "^4.11.1" memory-fs "^0.4.1" minimatch "^3.0.4" @@ -76,111 +78,108 @@ nopt "^4.0.1" opn "~5.1.0" portfinder "~1.0.12" - postcss-custom-properties "^6.1.0" - postcss-loader "^2.0.8" + postcss-import "^11.0.0" + postcss-loader "^2.0.10" postcss-url "^7.1.2" raw-loader "^0.5.1" resolve "^1.1.7" - rxjs "^5.5.2" - sass-loader "^6.0.3" + rxjs "^5.5.6" + sass-loader "^6.0.6" semver "^5.1.0" silent-error "^1.0.0" - source-map-loader "^0.2.0" source-map-support "^0.4.1" style-loader "^0.13.1" stylus "^0.54.5" stylus-loader "^3.0.1" - uglifyjs-webpack-plugin "~1.1.2" + uglifyjs-webpack-plugin "^1.1.5" url-loader "^0.6.2" webpack "~3.10.0" - webpack-concat-plugin "^1.4.2" webpack-dev-middleware "~1.12.0" - webpack-dev-server "~2.9.3" + webpack-dev-server "~2.11.0" webpack-merge "^4.1.0" webpack-sources "^1.0.0" webpack-subresource-integrity "^1.0.1" - zone.js "^0.8.14" optionalDependencies: - node-sass "^4.3.0" + node-sass "^4.7.2" -"@angular/common@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.0.1.tgz#43005ab3c8b8ffaf176aafb3b86ba931c3e4bdf9" +"@angular/common@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.2.8.tgz#25c81609305e4cee6734d90c8ff1132ab35f81bf" dependencies: tslib "^1.7.1" -"@angular/compiler-cli@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.0.1.tgz#526dc1bb394fb16ad916601eea9aa00eb44b4fff" +"@angular/compiler-cli@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.2.8.tgz#49e298dd8d5f7dde54045ff3c1acd9f3ca62e70b" dependencies: chokidar "^1.4.2" minimist "^1.2.0" reflect-metadata "^0.1.2" - tsickle "^0.24.0" + tsickle "^0.27.2" -"@angular/compiler@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.0.1.tgz#7fd4c7fa4bbbef4c146962fa946b827330a6c8ed" +"@angular/compiler@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.2.8.tgz#534315d0c700d57c2eb7cc7fff4f180213c8633f" dependencies: tslib "^1.7.1" -"@angular/core@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.0.1.tgz#a4a74afc7e2058d30b8263eb6d66daace9f427ba" +"@angular/core@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.2.8.tgz#d2e7657f2c5ce7f050e8f438dcc420c9bebca10a" dependencies: tslib "^1.7.1" -"@angular/flex-layout@2.0.0-beta.10-4905443": - version "2.0.0-beta.10-4905443" - resolved "https://registry.yarnpkg.com/@angular/flex-layout/-/flex-layout-2.0.0-beta.10-4905443.tgz#c62915c8f9a379d1da4800c12e3751e32cc7f855" +"@angular/flex-layout@2.0.0-beta.12": + version "2.0.0-beta.12" + resolved "https://registry.yarnpkg.com/@angular/flex-layout/-/flex-layout-2.0.0-beta.12.tgz#80970dc1d60f27fa41537659926f3238f759f343" dependencies: tslib "^1.7.1" -"@angular/forms@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.0.1.tgz#69f303c4c13da3caa0de63437588388b6ad62b21" +"@angular/forms@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.2.8.tgz#eda3c0ee2cb085581bb9bf8ac3c7b463b082b643" dependencies: tslib "^1.7.1" -"@angular/http@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.0.1.tgz#350cbdf63cfac8939613d753ff071ed58a60561b" +"@angular/http@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.2.8.tgz#be51315032515f8699f8b810ebf3ca78c0cc3759" dependencies: tslib "^1.7.1" -"@angular/language-service@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-5.0.1.tgz#869e09dbd6e3d95c117c062d21dd1fd920ad44d6" +"@angular/language-service@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-5.2.8.tgz#9dcc809ac4595e8b740971cf7f68a507ed5e4ff4" -"@angular/material@^5.0.0": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@angular/material/-/material-5.0.4.tgz#8efb2fb12023711dbdaac5dc7b588096b40b0f64" +"@angular/material@^5.2.0": + version "5.2.4" + resolved "https://registry.yarnpkg.com/@angular/material/-/material-5.2.4.tgz#9e823798324283d23ea839156fac5bcb73443d55" dependencies: tslib "^1.7.1" -"@angular/platform-browser-dynamic@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.0.1.tgz#16db67d52d4531563ab15429c6bdfe18bc1bedc8" +"@angular/platform-browser-dynamic@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.8.tgz#f2a620694ad771bf466d2bd752ca3a657ea7d46c" dependencies: tslib "^1.7.1" -"@angular/platform-browser@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.0.1.tgz#14895dd30ed2a30ee7b99c76b764748f46c1a862" +"@angular/platform-browser@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.2.8.tgz#253264887429ad71057882bcbc5cfda1fa4bbc06" dependencies: tslib "^1.7.1" -"@angular/platform-server@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.0.1.tgz#005a64eb657e670d265fdfae9473f19a764f8737" +"@angular/platform-server@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.2.8.tgz#215a32d9d23f1c7d3b64c219db3c43da7e6f92ac" dependencies: domino "^1.0.29" tslib "^1.7.1" xhr2 "^0.1.4" -"@angular/router@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.0.1.tgz#9ac08f29302ef60cdfd3c7810d96c265dec463d6" +"@angular/router@^5.2.0": + version "5.2.8" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.2.8.tgz#10e906803e5e71d42d157b0ccd78e773d1e316fc" dependencies: tslib "^1.7.1" @@ -188,9 +187,9 @@ version "1.1.0" resolved "https://registry.yarnpkg.com/@ngtools/json-schema/-/json-schema-1.1.0.tgz#c3a0c544d62392acc2813a42c8a0dc6f58f86922" -"@ngtools/webpack@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.9.0.tgz#ef395c45be2de9beb93a2b9f5f171d28c344eb10" +"@ngtools/webpack@1.9.7": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.9.7.tgz#ef15b90142ddf2a2c9072fe3d58c6bf500163fe5" dependencies: chalk "~2.2.0" enhanced-resolve "^3.1.0" @@ -199,20 +198,17 @@ semver "^5.3.0" source-map "^0.5.6" tree-kill "^1.0.0" + webpack-sources "^1.1.0" -"@ngx-translate/core@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.0.1.tgz#000f2d863c4c94c818e1416ef43cca2c5c0c5848" +"@ngx-translate/core@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.1.1.tgz#ae103928836b8a9e069fd2e2e76fa2198cc7e628" -"@schematics/angular@~0.1.10": - version "0.1.11" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.11.tgz#b5f15320bbb60969d66c76a8ef6545058ac81ece" +"@schematics/angular@0.1.17": + version "0.1.17" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.17.tgz#084a7cbe2de6f94a856bd08d95c9d35ef8905e2b" dependencies: - "@angular-devkit/core" "0.0.22" - -"@schematics/schematics@0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@schematics/schematics/-/schematics-0.0.11.tgz#c8f70f270ed38f29b2873248126fd59abd635862" + typescript "~2.6.2" "@types/d3-array@*": version "1.2.1" @@ -394,10 +390,14 @@ version "1.0.3" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.3.tgz#fbcf7fa5eb6dd108d51385cc6987ec1f24214523" -"@types/jasmine@*", "@types/jasmine@~2.5.53": +"@types/jasmine@*": version "2.5.54" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.54.tgz#a6b5f2ae2afb6e0307774e8c7c608e037d491c63" +"@types/jasmine@~2.8.3": + version "2.8.6" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e" + "@types/jasminewd2@~2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.3.tgz#0d2886b0cbdae4c0eeba55e30792f584bf040a95" @@ -416,23 +416,52 @@ version "2.53.42" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz#74cb77fb6052edaff2a8984ddafd88d419f25cac" +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + +JSONStream@^1.0.3: + version "1.3.2" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + abbrev@1: version "1.1.0" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" -accepts@1.3.3, accepts@~1.3.3: +accepts@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" dependencies: mime-types "~2.1.11" negotiator "0.6.1" +accepts@~1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" dependencies: acorn "^4.0.3" +acorn-node@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.3.0.tgz#5f86d73346743810ef1269b901dbcbded020861b" + dependencies: + acorn "^5.4.1" + xtend "^4.0.1" + acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" @@ -441,6 +470,14 @@ acorn@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7" +acorn@^5.2.1, acorn@^5.4.1: + version "5.5.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" + +addressparser@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" + adm-zip@0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.4.tgz#a61ed5ae6905c3aea58b3a657d25033091052736" @@ -468,6 +505,10 @@ ajv-keywords@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" +ajv-keywords@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" + ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -484,6 +525,23 @@ ajv@^5.0.0, ajv@^5.1.5: json-schema-traverse "^0.3.0" json-stable-stringify "^1.0.1" +ajv@^5.1.0, ajv@~5.5.1: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +ajv@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.1.tgz#28a6abc493a2abe0fb4c8507acaedb43fa550671" + dependencies: + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -500,9 +558,19 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -angular2-toaster@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/angular2-toaster/-/angular2-toaster-4.0.1.tgz#587aef6df903cf5e2b7eba4047b54ec9a21b8e3b" +amqplib@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63" + dependencies: + bitsyntax "~0.0.4" + bluebird "^3.4.6" + buffer-more-ints "0.0.2" + readable-stream "1.x >=1.1.9" + safe-buffer "^5.0.1" + +angular2-toaster@5.0.0-alpha.1: + version "5.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/angular2-toaster/-/angular2-toaster-5.0.0-alpha.1.tgz#24b5435c8c4ab5180c6071f9939ee13f5532428c" angular2-uuid@^1.1.1: version "1.1.1" @@ -530,6 +598,12 @@ ansi-styles@^3.1.0: dependencies: color-convert "^1.9.0" +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" @@ -537,6 +611,13 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + app-root-path@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46" @@ -574,10 +655,22 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -597,6 +690,14 @@ array-includes@^3.0.3: define-properties "^1.1.2" es-abstract "^1.7.0" +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + array-slice@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" @@ -615,9 +716,13 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -arraybuffer.slice@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" arrify@^1.0.0: version "1.0.1" @@ -647,12 +752,26 @@ assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" -assert@^1.1.1: +assert@^1.1.1, assert@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" dependencies: util "0.10.3" +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +ast-types@0.x.x: + version "0.11.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.2.tgz#cc4e1d15a36b39979a1986fe1e91321cbfae7783" + +astw@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917" + dependencies: + acorn "^4.0.3" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -661,25 +780,35 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" -async@^0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.1.2, async@^2.1.4, async@^2.1.5, async@^2.4.1: +async@^2.1.2, async@^2.1.4, async@^2.4.1: version "2.5.0" resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d" dependencies: lodash "^4.14.0" +async@~2.1.2: + version "2.1.5" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" -autoprefixer@^6.3.1, autoprefixer@^6.5.3: +atob@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + +autoprefixer@^6.3.1: version "6.7.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" dependencies: @@ -690,14 +819,35 @@ autoprefixer@^6.3.1, autoprefixer@^6.5.3: postcss "^5.2.16" postcss-value-parser "^3.2.3" +autoprefixer@^7.2.3: + version "7.2.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.2.6.tgz#256672f86f7c735da849c4f07d008abb056067dc" + dependencies: + browserslist "^2.11.3" + caniuse-lite "^1.0.30000805" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^6.0.17" + postcss-value-parser "^3.2.3" + aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" -aws4@^1.2.1: +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +axios@^0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053" + dependencies: + follow-redirects "1.0.0" + babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -793,6 +943,18 @@ base64id@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -817,6 +979,18 @@ binary-extensions@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" +bitsyntax@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82" + dependencies: + buffer-more-ints "0.0.2" + +bl@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" + dependencies: + readable-stream "~2.0.5" + blob@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" @@ -837,7 +1011,7 @@ bluebird@^3.3.0, bluebird@^3.4.7: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" -bluebird@^3.5.0, bluebird@^3.5.1: +bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -845,6 +1019,21 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" +body-parser@1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + body-parser@^1.16.1: version "1.17.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee" @@ -881,6 +1070,18 @@ boom@2.x.x: dependencies: hoek "2.x.x" +boom@4.x.x: + version "4.3.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -902,10 +1103,44 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.0, braces@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + define-property "^1.0.0" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + kind-of "^6.0.2" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" +browser-pack@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.4.tgz#9a73beb3b48f9e36868be007b64400102c04a99f" + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.8.0" + defined "^1.0.0" + safe-buffer "^5.1.1" + through2 "^2.0.0" + umd "^3.0.0" + +browser-resolve@^1.11.0, browser-resolve@^1.7.0: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + dependencies: + resolve "1.1.7" + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.0.8" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.8.tgz#c8fa3b1b7585bb7ba77c5560b60996ddec6d5309" @@ -958,6 +1193,64 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" +browserify-zlib@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserify@^14.5.0: + version "14.5.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5" + dependencies: + JSONStream "^1.0.3" + assert "^1.4.0" + browser-pack "^6.0.1" + browser-resolve "^1.11.0" + browserify-zlib "~0.2.0" + buffer "^5.0.2" + cached-path-relative "^1.0.0" + concat-stream "~1.5.1" + console-browserify "^1.1.0" + constants-browserify "~1.0.0" + crypto-browserify "^3.0.0" + defined "^1.0.0" + deps-sort "^2.0.0" + domain-browser "~1.1.0" + duplexer2 "~0.1.2" + events "~1.1.0" + glob "^7.1.0" + has "^1.0.0" + htmlescape "^1.1.0" + https-browserify "^1.0.0" + inherits "~2.0.1" + insert-module-globals "^7.0.0" + labeled-stream-splicer "^2.0.0" + module-deps "^4.0.8" + os-browserify "~0.3.0" + parents "^1.0.1" + path-browserify "~0.0.0" + process "~0.11.0" + punycode "^1.3.2" + querystring-es3 "~0.2.0" + read-only-stream "^2.0.0" + readable-stream "^2.0.2" + resolve "^1.1.4" + shasum "^1.0.0" + shell-quote "^1.6.1" + stream-browserify "^2.0.0" + stream-http "^2.0.0" + string_decoder "~1.0.0" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^2.0.0" + timers-browserify "^1.0.1" + tty-browserify "~0.0.0" + url "~0.11.0" + util "~0.10.1" + vm-browserify "~0.0.1" + xtend "^4.0.0" + browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: version "1.7.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" @@ -965,10 +1258,21 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" +browserslist@^2.11.3: + version "2.11.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.3.tgz#fe36167aed1bbcde4827ebfe71347a2cc70b99b2" + dependencies: + caniuse-lite "^1.0.30000792" + electron-to-chromium "^1.3.30" + buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" +buffer-more-ints@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c" + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -981,7 +1285,26 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.0.0: +buffer@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.1.0.tgz#c913e43678c7cb7c8bd16afbcddb6c5505e8f9fe" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +buildmail@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72" + dependencies: + addressparser "1.0.1" + libbase64 "0.1.0" + libmime "3.0.0" + libqp "1.1.0" + nodemailer-fetch "1.6.0" + nodemailer-shared "1.1.0" + punycode "1.4.1" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -997,6 +1320,10 @@ bytes@2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + cacache@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.2.tgz#105a93a162bbedf3a25da42e1939ed99ffb145f8" @@ -1015,6 +1342,24 @@ cacache@^10.0.1: unique-filename "^1.1.0" y18n "^3.2.1" +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cached-path-relative@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7" + callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" @@ -1062,6 +1407,14 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000726" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000726.tgz#9bb742f8d026a62df873bc03c06843d2255b60d7" +caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805: + version "1.0.30000813" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000813.tgz#7b25e27fdfb8d133f3c932b01f77452140fcc6c9" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1099,6 +1452,14 @@ chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@~2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.2.2.tgz#4403f5cf18f35c05f51fbdf152bf588f956cf7cb" @@ -1107,10 +1468,6 @@ chalk@~2.2.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - chart.js@2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.7.1.tgz#ae90b4aa4ff1f02decd6b1a2a8dabfd73c9f9886" @@ -1138,7 +1495,7 @@ chartjs-color@~2.2.0: chartjs-color-string "^0.5.0" color-convert "^0.5.3" -chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.6.0, chokidar@^1.7.0: +chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: @@ -1153,6 +1510,24 @@ chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.6.0, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +chokidar@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.0" + optionalDependencies: + fsevents "^1.0.0" + chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" @@ -1168,12 +1543,25 @@ circular-dependency-plugin@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-4.3.0.tgz#2a12824e584546e1aeea5865b7bf234a11c4a695" +circular-json@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f" + clap@^1.0.9: version "1.2.0" resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.0.tgz#59c90fe3e137104746ff19469a27a634ff68c857" dependencies: chalk "^1.1.3" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + classlist.js@^1.1.20150312: version "1.1.20150312" resolved "https://registry.yarnpkg.com/classlist.js/-/classlist.js-1.1.20150312.tgz#1d70842f7022f08d9ac086ce69e5b250f2c57789" @@ -1200,14 +1588,14 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -clone-deep@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" +clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" dependencies: for-own "^1.0.0" - is-plain-object "^2.0.1" - kind-of "^3.2.2" - shallow-clone "^0.1.2" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" clone@^1.0.2: version "1.0.2" @@ -1221,6 +1609,10 @@ co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +co@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda" + coa@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" @@ -1242,6 +1634,13 @@ codelyzer@^4.0.1: source-map "^0.5.6" sprintf-js "^1.0.3" +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" @@ -1278,7 +1677,7 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" -colors@1.1.2, colors@^1.1.0, colors@^1.1.2, colors@~1.1.2: +colors@1.1.2, colors@^1.1.0, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" @@ -1288,6 +1687,30 @@ combine-lists@^1.0.0: dependencies: lodash "^4.5.0" +combine-source-map@~0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" + +combine-source-map@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" + +combined-stream@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -1298,6 +1721,10 @@ commander@2, commander@2.11.x, commander@^2.9.0, commander@~2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" +commander@^2.12.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.0.tgz#ad2a23a1c3b036e392469b8012cec6b33b4c1322" + commander@~2.12.1: version "2.12.2" resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" @@ -1316,11 +1743,7 @@ component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" -component-emitter@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" - -component-emitter@1.2.1: +component-emitter@1.2.1, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -1358,6 +1781,14 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" +concat-stream@~1.5.0, concat-stream@~1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + connect-history-api-fallback@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" @@ -1381,7 +1812,7 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -constants-browserify@^1.0.0: +constants-browserify@^1.0.0, constants-browserify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -1393,9 +1824,17 @@ content-type@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" -convert-source-map@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +convert-source-map@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +convert-source-map@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" cookie-signature@1.0.6: version "1.0.6" @@ -1416,6 +1855,10 @@ copy-concurrently@^1.0.0: rimraf "^2.5.4" run-queue "^1.0.0" +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + copy-webpack-plugin@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.2.0.tgz#252bb94597f96399d23d7fad355f8d3a661ac096" @@ -1497,16 +1940,34 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" dependencies: boom "2.x.x" +cryptiles@3.x.x: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + dependencies: + boom "5.x.x" + +crypto-browserify@^3.0.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + crypto-browserify@^3.11.0: version "3.11.1" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f" @@ -1730,13 +2191,13 @@ d3-format@1: version "1.2.0" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.0.tgz#6b480baa886885d4651dc248a8f4ac9da16db07a" -d3-format@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f" +d3-format@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a" -d3-geo@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.0.tgz#15c7d7a8ea9346e59ed150dc7b1f7f95479056e9" +d3-geo@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356" dependencies: d3-array "1" @@ -1797,13 +2258,24 @@ d3-scale@1.0.7: d3-time "1" d3-time-format "2" +d3-scale@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.0.0.tgz#fd8ac78381bc2ed741d8c71770437a5e0549a5a5" + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + d3-selection@1, d3-selection@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.1.0.tgz#1998684896488f839ca0372123da34f1d318809c" -d3-selection@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88" +d3-selection@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.0.tgz#d53772382d3dc4f7507bfb28bcd2d6aed2a0ad6d" d3-shape@1.2.0: version "1.2.0" @@ -1871,9 +2343,9 @@ d3-zoom@1.7.1: d3-selection "1" d3-transition "1" -d3@4.12.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.0.tgz#75eccb39ea40f6018de8cfa2752905bee7daa46f" +d3@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-4.13.0.tgz#ab236ff8cf0cfc27a81e69bf2fb7518bc9b4f33d" dependencies: d3-array "1.2.1" d3-axis "1.0.8" @@ -1886,8 +2358,8 @@ d3@4.12.0: d3-dsv "1.0.8" d3-ease "1.0.3" d3-force "1.1.0" - d3-format "1.2.1" - d3-geo "1.9.0" + d3-format "1.2.2" + d3-geo "1.9.1" d3-hierarchy "1.1.5" d3-interpolate "1.1.6" d3-path "1.0.5" @@ -1897,7 +2369,7 @@ d3@4.12.0: d3-random "1.1.0" d3-request "1.0.6" d3-scale "1.0.7" - d3-selection "1.2.0" + d3-selection "1.3.0" d3-shape "1.2.0" d3-time "1.0.8" d3-time-format "2.1.1" @@ -1918,10 +2390,18 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + date-fns@^2.0.0-alpha.7: version "2.0.0-alpha.7" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.7.tgz#245ad16f95764eababfb2c0a41fd5d033c20e57a" +date-format@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8" + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -1932,34 +2412,38 @@ debug@*, debug@2, debug@2.6.8, debug@^2.2.0, debug@^2.6.3, debug@^2.6.6, debug@^ dependencies: ms "2.0.0" -debug@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - dependencies: - ms "0.7.1" - -debug@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" - dependencies: - ms "0.7.2" - debug@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" dependencies: ms "2.0.0" -debug@^3.1.0: +debug@2.6.9, debug@^2.3.3, debug@~2.6.4, debug@~2.6.6: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: ms "2.0.0" +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -1968,6 +2452,10 @@ deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + default-require-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" @@ -1981,10 +2469,37 @@ define-properties@^1.1.2: foreach "^2.0.5" object-keys "^1.0.8" +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" +degenerator@~1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + del@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -2024,6 +2539,15 @@ depd@1.1.1, depd@~1.1.0, depd@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" +deps-sort@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5" + dependencies: + JSONStream "^1.0.3" + shasum "^1.0.0" + subarg "^1.0.0" + through2 "^2.0.0" + des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" @@ -2045,6 +2569,13 @@ detect-node@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" +detective@^4.0.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" + dependencies: + acorn "^5.2.1" + defined "^1.0.0" + di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" @@ -2100,7 +2631,7 @@ dom-serializer@0: domelementtype "~1.1.1" entities "~1.1.1" -domain-browser@^1.1.1: +domain-browser@^1.1.1, domain-browser@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" @@ -2135,6 +2666,16 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" +double-ended-queue@^2.1.0-0: + version "2.1.0-0" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + +duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + dependencies: + readable-stream "^2.0.2" + duplexify@^3.1.2, duplexify@^3.4.2: version "3.5.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd" @@ -2162,6 +2703,10 @@ electron-to-chromium@^1.2.7: version "1.3.21" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2" +electron-to-chromium@^1.3.30: + version "1.3.36" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.36.tgz#0eabf71a9ebea9013fb1cc35a390e068624f27e8" + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2192,44 +2737,44 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -engine.io-client@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" +engine.io-client@~3.1.0: + version "3.1.6" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.6.tgz#5bdeb130f8b94a50ac5cbeb72583e7a4a063ddfd" dependencies: component-emitter "1.2.1" component-inherit "0.0.3" - debug "2.3.3" - engine.io-parser "1.3.2" + debug "~3.1.0" + engine.io-parser "~2.1.1" has-cors "1.1.0" indexof "0.0.1" - parsejson "0.0.3" parseqs "0.0.5" parseuri "0.0.5" - ws "1.1.2" - xmlhttprequest-ssl "1.5.3" + ws "~3.3.1" + xmlhttprequest-ssl "~1.5.4" yeast "0.1.2" -engine.io-parser@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a" +engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196" dependencies: after "0.8.2" - arraybuffer.slice "0.0.6" + arraybuffer.slice "~0.0.7" base64-arraybuffer "0.1.5" blob "0.0.4" - has-binary "0.1.7" - wtf-8 "1.0.0" + has-binary2 "~1.0.2" -engine.io@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4" +engine.io@~3.1.0: + version "3.1.5" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.5.tgz#0e7ef9d690eb0b35597f1d4ad02a26ca2dba3845" dependencies: - accepts "1.3.3" + accepts "~1.3.4" base64id "1.0.0" cookie "0.3.1" - debug "2.3.3" - engine.io-parser "1.3.2" - ws "1.1.2" + debug "~3.1.0" + engine.io-parser "~2.1.0" + ws "~3.3.1" + optionalDependencies: + uws "~9.14.0" enhanced-resolve@^3.1.0, enhanced-resolve@^3.4.0: version "3.4.1" @@ -2338,6 +2883,17 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escodegen@1.x.x: + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" @@ -2347,6 +2903,10 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +esprima@3.x.x, esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -2362,7 +2922,7 @@ esrecurse@^4.1.0: estraverse "^4.1.0" object-assign "^4.0.1" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" @@ -2370,9 +2930,9 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" -etag@~1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" event-emitter@~0.3.5: version "0.3.5" @@ -2385,7 +2945,7 @@ eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" -events@^1.0.0: +events@^1.0.0, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -2432,6 +2992,18 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" @@ -2452,40 +3024,55 @@ exports-loader@^0.6.3: loader-utils "^1.0.2" source-map "0.5.x" -express@^4.13.3: - version "4.15.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1" +express@^4.16.2: + version "4.16.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" dependencies: - accepts "~1.3.3" + accepts "~1.3.4" array-flatten "1.1.1" + body-parser "1.18.2" content-disposition "0.5.2" - content-type "~1.0.2" + content-type "~1.0.4" cookie "0.3.1" cookie-signature "1.0.6" - debug "2.6.8" + debug "2.6.9" depd "~1.1.1" encodeurl "~1.0.1" escape-html "~1.0.3" - etag "~1.8.0" - finalhandler "~1.0.4" - fresh "0.5.0" + etag "~1.8.1" + finalhandler "1.1.0" + fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.1" + parseurl "~1.3.2" path-to-regexp "0.1.7" - proxy-addr "~1.1.5" - qs "6.5.0" + proxy-addr "~2.0.2" + qs "6.5.1" range-parser "~1.2.0" - send "0.15.4" - serve-static "1.12.4" - setprototypeof "1.0.3" + safe-buffer "5.1.1" + send "0.16.1" + serve-static "1.13.1" + setprototypeof "1.1.0" statuses "~1.3.1" type-is "~1.6.15" - utils-merge "1.0.0" - vary "~1.1.1" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" -extend@3, extend@^3.0.0, extend@~3.0.0: +extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -2495,6 +3082,19 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + extract-text-webpack-plugin@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz#5f043eaa02f9750a9258b78c0a6e0dc1408fb2f7" @@ -2512,6 +3112,14 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" @@ -2535,6 +3143,10 @@ file-loader@^1.1.5: loader-utils "^1.0.2" schema-utils "^0.3.0" +file-uri-to-path@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -2556,7 +3168,16 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@1.0.4, finalhandler@~1.0.4: +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7" dependencies: @@ -2568,6 +3189,18 @@ finalhandler@1.0.4, finalhandler@~1.0.4: statuses "~1.3.1" unpipe "~1.0.0" +finalhandler@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + dependencies: + debug "2.6.9" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.3.1" + unpipe "~1.0.0" + find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -2600,11 +3233,17 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37" + dependencies: + debug "^2.2.0" + for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" -for-in@^1.0.1: +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2628,6 +3267,14 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" +form-data@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.11" + form-data@~2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" @@ -2636,13 +3283,27 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" +form-data@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" -fresh@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" from2@^2.1.0: version "2.3.0" @@ -2710,6 +3371,13 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2733,6 +3401,16 @@ gaze@^1.0.0: dependencies: globule "^1.0.0" +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -2745,6 +3423,21 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-uri@2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59" + dependencies: + data-uri-to-buffer "1" + debug "2" + extend "3" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "2" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2764,6 +3457,13 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + glob@7.0.x: version "7.0.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" @@ -2775,7 +3475,17 @@ glob@7.0.x: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -2845,6 +3555,19 @@ har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + har-validator@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" @@ -2852,17 +3575,24 @@ har-validator@~4.2.1: ajv "^4.9.1" har-schema "^1.0.5" +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" dependencies: ansi-regex "^2.0.0" -has-binary@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c" +has-binary2@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98" dependencies: - isarray "0.0.1" + isarray "2.0.1" has-cors@1.1.0: version "1.1.0" @@ -2876,11 +3606,42 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.1: +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" dependencies: @@ -2915,10 +3676,26 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" +hawk@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +hipchat-notifier@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e" + dependencies: + lodash "^4.0.0" + request "^2.0.0" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -2931,6 +3708,10 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hoek@4.x.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + hosted-git-info@^2.1.4: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" @@ -2976,6 +3757,10 @@ html-webpack-plugin@^2.29.0: pretty-error "^2.0.2" toposort "^1.0.0" +htmlescape@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" + htmlparser2@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" @@ -2989,7 +3774,7 @@ http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" -http-errors@~1.6.1, http-errors@~1.6.2: +http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: @@ -2998,6 +3783,14 @@ http-errors@~1.6.1, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" +http-proxy-agent@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" + dependencies: + agent-base "2" + debug "2" + extend "3" + http-proxy-middleware@~0.17.4: version "0.17.4" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" @@ -3022,11 +3815,34 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +httpntlm@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2" + dependencies: + httpreq ">=0.4.22" + underscore "~1.7.0" + +httpreq@>=0.4.22: + version "0.4.24" + resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-0.4.24.tgz#4335ffd82cd969668a39465c929ac61d6393627f" + https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -https-proxy-agent@^1.0.0: +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +https-proxy-agent@1, https-proxy-agent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" dependencies: @@ -3038,6 +3854,10 @@ iconv-lite@0.4, iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -3060,9 +3880,9 @@ image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" -import-local@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-0.1.1.tgz#b1179572aacdc11c6a91009fb430dbcab5f668a8" +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" dependencies: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" @@ -3089,6 +3909,14 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" +inflection@~1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" + +inflection@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.3.8.tgz#cbd160da9f75b14c3cc63578d4f396784bf3014e" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3108,6 +3936,25 @@ ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" +inline-source-map@~0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" + dependencies: + source-map "~0.5.3" + +insert-module-globals@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.2.tgz#012c56baa7d3307a8b417d4ec5270cf9741c18f4" + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.7.1" + concat-stream "~1.5.1" + is-buffer "^1.1.0" + lexical-scope "^1.2.0" + process "~0.11.0" + through2 "^2.0.0" + xtend "^4.0.0" + internal-ip@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" @@ -3132,18 +3979,34 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -ip@^1.1.0, ip@^1.1.5: +ip@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590" + +ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" -ipaddr.js@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3154,7 +4017,11 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1: +is-buffer@^1.1.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" @@ -3168,10 +4035,38 @@ is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" @@ -3186,10 +4081,16 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" @@ -3232,6 +4133,20 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + +is-my-json-valid@^2.12.4: + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + is-number@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" @@ -3248,6 +4163,16 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + dependencies: + is-number "^4.0.0" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -3268,7 +4193,7 @@ is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-object@^2.0.1: +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: @@ -3282,6 +4207,10 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -3310,11 +4239,15 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" -isarray@0.0.1: +isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -3322,7 +4255,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" -isbinaryfile@^3.0.0: +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + +isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -3336,7 +4273,7 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.1: +isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -3360,26 +4297,42 @@ istanbul-api@^1.1.8: mkdirp "^0.5.1" once "^1.4.0" -istanbul-instrumenter-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz#e5492900ab0bba835efa8024cb00be9b3eea2700" +istanbul-instrumenter-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz#9f553923b22360bac95e617aaba01add1f7db0b2" dependencies: - convert-source-map "^1.3.0" - istanbul-lib-instrument "^1.1.3" - loader-utils "^0.2.16" - object-assign "^4.1.0" + convert-source-map "^1.5.0" + istanbul-lib-instrument "^1.7.3" + loader-utils "^1.1.0" + schema-utils "^0.3.0" istanbul-lib-coverage@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" +istanbul-lib-coverage@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" + istanbul-lib-hook@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc" dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.1.3, istanbul-lib-instrument@^1.8.0: +istanbul-lib-instrument@^1.7.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.0.tgz#47f20bfed9b9cbbc45417d3c9aff37bfbacbd281" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.2.0" + semver "^5.3.0" + +istanbul-lib-instrument@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532" dependencies: @@ -3416,17 +4369,13 @@ istanbul-reports@^1.1.2: dependencies: handlebars "^4.0.3" -jasmine-core@~2.6.2: - version "2.6.4" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.4.tgz#dec926cd0a9fa287fb6db5c755fa487e74cecac5" - jasmine-core@~2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" -jasmine-spec-reporter@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.1.1.tgz#5a6d58ab5d61bea7309fbc279239511756b1b588" +jasmine-spec-reporter@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz#1d632aec0341670ad324f92ba84b4b32b35e9e22" dependencies: colors "1.1.2" @@ -3494,11 +4443,17 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stable-stringify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json3@3.3.2, json3@^3.3.2: +json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -3522,6 +4477,14 @@ jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -3531,19 +4494,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -karma-chrome-launcher@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf" +karma-chrome-launcher@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf" dependencies: fs-access "^1.0.0" which "^1.2.1" -karma-cli@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/karma-cli/-/karma-cli-1.0.1.tgz#ae6c3c58a313a1d00b45164c455b9b86ce17f960" - dependencies: - resolve "^1.1.6" - karma-coverage-istanbul-reporter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.3.0.tgz#d142cd9c55731c9e363ef7374e8ef1a31bebfadb" @@ -3567,12 +4524,13 @@ karma-source-map-support@^1.2.0: dependencies: source-map-support "^0.4.1" -karma@~1.7.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.1.tgz#85cc08e9e0a22d7ce9cca37c4a1be824f6a2b1ae" +karma@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.0.tgz#a02698dd7f0f05ff5eb66ab8f65582490b512e58" dependencies: bluebird "^3.3.0" body-parser "^1.16.1" + browserify "^14.5.0" chokidar "^1.4.1" colors "^1.1.0" combine-lists "^1.0.0" @@ -3585,8 +4543,8 @@ karma@~1.7.0: graceful-fs "^4.1.2" http-proxy "^1.13.0" isbinaryfile "^3.0.0" - lodash "^3.8.0" - log4js "^0.6.31" + lodash "^4.17.4" + log4js "^2.3.9" mime "^1.3.4" minimatch "^3.0.2" optimist "^0.6.1" @@ -3594,22 +4552,16 @@ karma@~1.7.0: range-parser "^1.2.0" rimraf "^2.6.0" safe-buffer "^5.0.1" - socket.io "1.7.3" - source-map "^0.5.3" - tmp "0.0.31" + socket.io "2.0.4" + source-map "^0.6.1" + tmp "0.0.33" useragent "^2.1.12" killable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" -kind-of@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" - dependencies: - is-buffer "^1.0.2" - -kind-of@^3.0.2, kind-of@^3.2.2: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: @@ -3621,14 +4573,32 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +labeled-stream-splicer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59" + dependencies: + inherits "^2.0.1" + isarray "~0.0.1" + stream-splicer "^2.0.0" lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" +lazy-cache@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" + dependencies: + set-getter "^0.1.0" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -3656,6 +4626,35 @@ less@^2.7.2: request "^2.72.0" source-map "^0.5.3" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lexical-scope@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4" + dependencies: + astw "^2.0.0" + +libbase64@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6" + +libmime@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.0.0.tgz#51a1a9e7448ecbd32cda54421675bb21bc093da6" + dependencies: + iconv-lite "0.4.15" + libbase64 "0.1.0" + libqp "1.1.0" + +libqp@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" + license-webpack-plugin@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.0.1.tgz#abeb3ab168a9930f2fd57311951dc094aaf33e45" @@ -3685,22 +4684,22 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@~0.2.2: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" +loader-utils@1.1.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" - object-assign "^4.0.1" -loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" +loader-utils@^0.2.15, loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" + object-assign "^4.0.1" locate-path@^2.0.0: version "2.0.0" @@ -3725,6 +4724,10 @@ lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" +lodash.memoize@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + lodash.mergewith@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" @@ -3737,20 +4740,40 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^3.8.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" -log4js@^0.6.31: - version "0.6.38" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" +lodash@^4.15.0: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + +log4js@^2.3.9: + version "2.5.3" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1" + dependencies: + circular-json "^0.5.1" + date-format "^1.2.0" + debug "^3.1.0" + semver "^5.3.0" + streamroller "^0.7.0" + optionalDependencies: + amqplib "^0.5.2" + axios "^0.15.3" + hipchat-notifier "^1.1.0" + loggly "^1.1.0" + mailgun-js "^0.7.0" + nodemailer "^2.5.0" + redis "^2.7.1" + slack-node "~0.2.0" + +loggly@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/loggly/-/loggly-1.1.1.tgz#0a0fc1d3fa3a5ec44fdc7b897beba2a4695cebee" dependencies: - readable-stream "~1.0.2" - semver "~4.3.3" + json-stringify-safe "5.0.x" + request "2.75.x" + timespan "2.3.x" loglevel@^1.4.1: version "1.5.0" @@ -3788,6 +4811,10 @@ lru-cache@^4.0.1, lru-cache@^4.1.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@~2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" + macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" @@ -3798,6 +4825,27 @@ magic-string@^0.22.3: dependencies: vlq "^0.2.1" +mailcomposer@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4" + dependencies: + buildmail "4.0.1" + libmime "3.0.0" + +mailgun-js@^0.7.0: + version "0.7.15" + resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb" + dependencies: + async "~2.1.2" + debug "~2.2.0" + form-data "~2.1.1" + inflection "~1.10.0" + is-stream "^1.1.0" + path-proxy "~1.0.0" + proxy-agent "~2.0.0" + q "~1.4.0" + tsscmp "~1.0.0" + make-dir@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" @@ -3808,10 +4856,20 @@ make-error@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + material-design-icons-iconfont@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/material-design-icons-iconfont/-/material-design-icons-iconfont-3.0.3.tgz#154a1084047d4e27237fa7f5a37e1075ceea6df2" @@ -3827,14 +4885,6 @@ md5.js@^1.3.4: hash-base "^3.0.0" inherits "^2.0.1" -md5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -3893,6 +4943,24 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.4: + version "3.1.9" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + miller-rabin@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" @@ -3904,23 +4972,33 @@ miller-rabin@^4.0.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-types@^2.1.11, mime-types@~2.1.17, mime-types@~2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: mime-db "~1.30.0" -mime@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +mime@1.4.1, mime@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" mime@^1.2.11, mime@^1.3.4: version "1.4.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.0.tgz#69e9e0db51d44f2a3b56e48b7817d7d137f1a343" -mime@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mime@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" mimic-fn@^1.0.0: version "1.1.0" @@ -3934,7 +5012,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -3944,7 +5022,7 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.1.3, minimist@^1.2.0: +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -3967,6 +5045,13 @@ mississippi@^1.3.0: stream-each "^1.1.0" through2 "^2.0.0" +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mixin-object@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" @@ -3980,6 +5065,26 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd dependencies: minimist "0.0.8" +module-deps@^4.0.8: + version "4.1.1" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd" + dependencies: + JSONStream "^1.0.3" + browser-resolve "^1.7.0" + cached-path-relative "^1.0.0" + concat-stream "~1.5.0" + defined "^1.0.0" + detective "^4.0.0" + duplexer2 "^0.1.2" + inherits "^2.0.1" + parents "^1.0.0" + readable-stream "^2.0.2" + resolve "^1.1.3" + stream-combiner2 "^1.1.1" + subarg "^1.0.0" + through2 "^2.0.0" + xtend "^4.0.0" + moment@~2.18.0: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" @@ -3999,10 +5104,6 @@ ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -4018,14 +5119,31 @@ multicast-dns@^6.0.1: dns-packet "^1.0.1" thunky "^0.1.0" -mydaterangepicker@^4.1.12: - version "4.1.12" - resolved "https://registry.yarnpkg.com/mydaterangepicker/-/mydaterangepicker-4.1.12.tgz#0d1e409e992f2b13a5c4f02faf8ea8103841e546" +mydaterangepicker@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/mydaterangepicker/-/mydaterangepicker-4.2.1.tgz#f063e4747016259b9ad882159efc1c7df09221cd" nan@^2.3.0, nan@^2.3.2: version "2.7.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + ncname@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" @@ -4036,6 +5154,14 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +neo-async@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" + +netmask@~1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + ng2-charts@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/ng2-charts/-/ng2-charts-1.6.0.tgz#108a2133ff62a8623895240fadbddbea2951f29d" @@ -4046,9 +5172,9 @@ ng2-cookies@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/ng2-cookies/-/ng2-cookies-1.0.12.tgz#3f3e613e0137b0649b705c678074b4bd08149ccc" -ngx-loading@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/ngx-loading/-/ngx-loading-1.0.9.tgz#d0ce8389dabb6b52c3013f1e3d1616b968f0ad1e" +ngx-loading@^1.0.14: + version "1.0.14" + resolved "https://registry.yarnpkg.com/ngx-loading/-/ngx-loading-1.0.14.tgz#19758c33ea3fa9bb96dca1f40ca19d4d86b8042c" no-case@^2.2.0: version "2.3.1" @@ -4130,9 +5256,9 @@ node-pre-gyp@^0.6.36: tar "^2.2.1" tar-pack "^3.4.0" -node-sass@^4.3.0: - version "4.5.3" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" +node-sass@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -4149,9 +5275,63 @@ node-sass@^4.3.0: nan "^2.3.2" node-gyp "^3.3.1" npmlog "^4.0.0" - request "^2.79.0" - sass-graph "^2.1.1" + request "~2.79.0" + sass-graph "^2.2.4" stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +node-uuid@~1.4.7: + version "1.4.8" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + +nodemailer-direct-transport@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86" + dependencies: + nodemailer-shared "1.1.0" + smtp-connection "2.12.0" + +nodemailer-fetch@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4" + +nodemailer-shared@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0" + dependencies: + nodemailer-fetch "1.6.0" + +nodemailer-smtp-pool@2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz#2eb94d6cf85780b1b4725ce853b9cbd5e8da8c72" + dependencies: + nodemailer-shared "1.1.0" + nodemailer-wellknown "0.1.10" + smtp-connection "2.12.0" + +nodemailer-smtp-transport@2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz#03d71c76314f14ac7dbc7bf033a6a6d16d67fb77" + dependencies: + nodemailer-shared "1.1.0" + nodemailer-wellknown "0.1.10" + smtp-connection "2.12.0" + +nodemailer-wellknown@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz#586db8101db30cb4438eb546737a41aad0cf13d5" + +nodemailer@^2.5.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-2.7.2.tgz#f242e649aeeae39b6c7ed740ef7b061c404d30f9" + dependencies: + libmime "3.0.0" + mailcomposer "4.0.1" + nodemailer-direct-transport "3.3.2" + nodemailer-shared "1.1.0" + nodemailer-smtp-pool "2.8.2" + nodemailer-smtp-transport "2.7.2" + socks "1.1.9" "nopt@2 || 3": version "3.0.6" @@ -4175,7 +5355,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1: +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: @@ -4227,11 +5407,11 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" -oauth-sign@~0.8.1: +oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@4.1.0, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" @@ -4239,10 +5419,24 @@ object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -4250,6 +5444,12 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + obuf@^1.0.0, obuf@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" @@ -4283,6 +5483,17 @@ optimist@^0.6.1, optimist@~0.6.0: minimist "~0.0.1" wordwrap "~0.0.2" +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" @@ -4297,6 +5508,10 @@ os-browserify@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" +os-browserify@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -4315,7 +5530,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -4344,10 +5559,38 @@ p-map@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a" +pac-proxy-agent@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d" + dependencies: + agent-base "2" + debug "2" + extend "3" + get-uri "2" + http-proxy-agent "1" + https-proxy-agent "1" + pac-resolver "~2.0.0" + raw-body "2" + socks-proxy-agent "2" + +pac-resolver@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd" + dependencies: + co "~3.0.6" + degenerator "~1.0.2" + ip "1.0.1" + netmask "~1.0.4" + thunkify "~2.1.1" + pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + parallel-transform@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" @@ -4362,6 +5605,12 @@ param-case@2.1.x: dependencies: no-case "^2.2.0" +parents@^1.0.0, parents@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" + dependencies: + path-platform "~0.11.15" + parse-asn1@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" @@ -4387,12 +5636,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parsejson@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" - dependencies: - better-assert "~1.0.0" - parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -4409,10 +5652,22 @@ parseurl@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" -path-browserify@0.0.0: +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0, path-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -4439,6 +5694,16 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-platform@~0.11.15: + version "0.11.15" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + +path-proxy@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e" + dependencies: + inflection "~1.3.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4471,6 +5736,10 @@ performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4503,6 +5772,10 @@ portfinder@^1.0.9, portfinder@~1.0.12: debug "^2.2.0" mkdirp "0.5.x" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + postcss-calc@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" @@ -4526,13 +5799,6 @@ postcss-convert-values@^2.3.4: postcss "^5.0.11" postcss-value-parser "^3.1.2" -postcss-custom-properties@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-6.2.0.tgz#5d929a7f06e9b84e0f11334194c0ba9a30acfbe9" - dependencies: - balanced-match "^1.0.0" - postcss "^6.0.13" - postcss-discard-comments@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" @@ -4571,6 +5837,15 @@ postcss-filter-plugins@^2.0.0: postcss "^5.0.4" uniqid "^4.0.0" +postcss-import@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-11.1.0.tgz#55c9362c9192994ec68865d224419df1db2981f0" + dependencies: + postcss "^6.0.1" + postcss-value-parser "^3.2.3" + read-cache "^1.0.0" + resolve "^1.1.7" + postcss-load-config@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" @@ -4594,14 +5869,14 @@ postcss-load-plugins@^2.3.0: cosmiconfig "^2.1.1" object-assign "^4.1.0" -postcss-loader@^2.0.8: - version "2.0.9" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.9.tgz#001fdf7bfeeb159405ee61d1bb8e59b528dbd309" +postcss-loader@^2.0.10: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.1.tgz#208935af3b1d65e1abb1a870a912dd12e7b36895" dependencies: loader-utils "^1.1.0" postcss "^6.0.0" postcss-load-config "^1.2.0" - schema-utils "^0.3.0" + schema-utils "^0.4.0" postcss-merge-idents@^2.1.5: version "2.1.7" @@ -4790,7 +6065,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" -postcss@^6.0.0, postcss@^6.0.13: +postcss@^6.0.0: version "6.0.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885" dependencies: @@ -4806,6 +6081,18 @@ postcss@^6.0.1: source-map "^0.5.7" supports-color "^4.4.0" +postcss@^6.0.17: + version "6.0.19" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555" + dependencies: + chalk "^2.3.1" + source-map "^0.6.1" + supports-color "^5.2.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -4825,7 +6112,11 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -process@^0.11.0: +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.0, process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -4859,12 +6150,25 @@ protractor@~5.1.2: webdriver-js-extender "^1.0.0" webdriver-manager "^12.0.6" -proxy-addr@~1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" +proxy-addr@~2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.6.0" + +proxy-agent@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" dependencies: - forwarded "~0.1.0" - ipaddr.js "1.4.0" + agent-base "2" + debug "2" + extend "3" + http-proxy-agent "1" + https-proxy-agent "1" + lru-cache "~2.6.5" + pac-proxy-agent "1" + socks-proxy-agent "2" prr@~0.0.0: version "0.0.0" @@ -4903,11 +6207,11 @@ punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" -punycode@^1.2.4, punycode@^1.4.1: +punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" -q@1.4.1, q@^1.1.2, q@^1.4.1: +q@1.4.1, q@^1.1.2, q@^1.4.1, q@~1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" @@ -4919,9 +6223,17 @@ qs@6.4.0, qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" -qs@6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" +qs@6.5.1, qs@~6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +qs@~6.2.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" + +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" query-string@^4.1.0: version "4.3.4" @@ -4930,7 +6242,7 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@^0.2.0: +querystring-es3@^0.2.0, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -4959,10 +6271,32 @@ randombytes@^2.0.0, randombytes@^2.0.1: dependencies: safe-buffer "^5.1.0" +randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" +raw-body@2, raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + raw-body@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" @@ -4984,6 +6318,18 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + dependencies: + pify "^2.3.0" + +read-only-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" + dependencies: + readable-stream "^2.0.2" + read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -5026,7 +6372,7 @@ read-pkg@^2.0.0: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@1.0, readable-stream@~1.0.2: +readable-stream@1.0: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" dependencies: @@ -5035,22 +6381,70 @@ readable-stream@1.0, readable-stream@~1.0.2: isarray "0.0.1" string_decoder "~0.10.x" -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" +readable-stream@1.1.x, "readable-stream@1.x >=1.1.9": + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -redent@^1.0.0: - version "1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@2, readable-stream@^2.3.0, readable-stream@^2.3.3: + version "2.3.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" + dependencies: + 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.0.3" + util-deprecate "~1.0.1" + +readable-stream@~2.0.0, readable-stream@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + 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" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +redent@^1.0.0: + version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" dependencies: indent-string "^2.1.0" strip-indent "^1.0.1" +redis-commands@^1.2.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.5.tgz#4495889414f1e886261180b1442e7295602d83a2" + +redis-parser@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" + +redis@^2.7.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" + dependencies: + double-ended-queue "^2.1.0-0" + redis-commands "^1.2.0" + redis-parser "^2.6.0" + reduce-css-calc@^1.2.6: version "1.3.0" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" @@ -5083,6 +6477,13 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -5127,7 +6528,7 @@ repeat-string@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -5137,7 +6538,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2, request@^2.72.0, request@^2.78.0, request@^2.79.0, request@^2.81.0: +request@2, request@^2.72.0, request@^2.78.0, request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -5164,6 +6565,93 @@ request@2, request@^2.72.0, request@^2.78.0, request@^2.79.0, request@^2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@2.75.x: + version "2.75.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + bl "~1.1.2" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.0.0" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.2.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + +request@^2.0.0, request@^2.74.0: + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +requestretry@^1.2.2: + version "1.13.0" + resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94" + dependencies: + extend "^3.0.0" + lodash "^4.15.0" + request "^2.74.0" + when "^3.7.7" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -5190,12 +6678,30 @@ resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2: +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.1.3, resolve@^1.1.4: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + dependencies: + path-parse "^1.0.5" + +resolve@^1.1.7, resolve@^1.3.2: version "1.4.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" dependencies: path-parse "^1.0.5" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -5233,17 +6739,23 @@ rxjs-websockets@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/rxjs-websockets/-/rxjs-websockets-4.0.0.tgz#a8d06c74b8629a9f9d56450eda5c3177542c80f4" -rxjs@^5.5.2: - version "5.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3" +rxjs@^5.5.6: + version "5.5.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.6.tgz#e31fb96d6fd2ff1fd84bcea8ae9c02d007179c02" dependencies: - symbol-observable "^1.0.1" + symbol-observable "1.0.1" safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -sass-graph@^2.1.1: +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" dependencies: @@ -5252,14 +6764,14 @@ sass-graph@^2.1.1: scss-tokenizer "^0.2.3" yargs "^7.0.0" -sass-loader@^6.0.3: - version "6.0.6" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" +sass-loader@^6.0.6: + version "6.0.7" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.7.tgz#dd2fdb3e7eeff4a53f35ba6ac408715488353d00" dependencies: - async "^2.1.5" - clone-deep "^0.3.0" + clone-deep "^2.0.1" loader-utils "^1.0.1" lodash.tail "^4.1.1" + neo-async "^2.5.0" pify "^3.0.0" saucelabs@~1.3.0: @@ -5286,6 +6798,13 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" +schema-utils@^0.4.0: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + schema-utils@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.3.tgz#e2a594d3395834d5e15da22b48be13517859458e" @@ -5339,10 +6858,6 @@ semver-dsl@^1.0.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" -semver@~4.3.3: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" @@ -5351,19 +6866,19 @@ semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -send@0.15.4: - version "0.15.4" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9" +send@0.16.1: + version "0.16.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" dependencies: - debug "2.6.8" + debug "2.6.9" depd "~1.1.1" destroy "~1.0.4" encodeurl "~1.0.1" escape-html "~1.0.3" - etag "~1.8.0" - fresh "0.5.0" + etag "~1.8.1" + fresh "0.5.2" http-errors "~1.6.2" - mime "1.3.4" + mime "1.4.1" ms "2.0.0" on-finished "~2.3.0" range-parser "~1.2.0" @@ -5385,23 +6900,47 @@ serve-index@^1.7.2: mime-types "~2.1.15" parseurl "~1.3.1" -serve-static@1.12.4: - version "1.12.4" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961" +serve-static@1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.15.4" + parseurl "~1.3.2" + send "0.16.1" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" +set-getter@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" + dependencies: + to-object-path "^0.3.0" + set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -5410,21 +6949,38 @@ setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.8" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" dependencies: inherits "^2.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" +sha.js@~2.4.4: + version "2.4.10" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" dependencies: is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" + kind-of "^5.0.0" mixin-object "^2.0.1" +shasum@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" + dependencies: + json-stable-stringify "~0.0.0" + sha.js "~2.4.4" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -5435,6 +6991,15 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -5445,55 +7010,102 @@ silent-error@^1.0.0: dependencies: debug "^2.2.0" +slack-node@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/slack-node/-/slack-node-0.2.0.tgz#de4b8dddaa8b793f61dbd2938104fdabf37dfa30" + dependencies: + requestretry "^1.2.2" + +smart-buffer@^1.0.13, smart-buffer@^1.0.4: + version "1.1.15" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" + +smtp-connection@2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1" + dependencies: + httpntlm "1.6.1" + nodemailer-shared "1.1.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^2.0.0" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" dependencies: hoek "2.x.x" -socket.io-adapter@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b" +sntp@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" dependencies: - debug "2.3.3" - socket.io-parser "2.3.1" + hoek "4.x.x" + +socket.io-adapter@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b" -socket.io-client@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377" +socket.io-client@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.4.tgz#0918a552406dc5e540b380dcd97afc4a64332f8e" dependencies: backo2 "1.0.2" + base64-arraybuffer "0.1.5" component-bind "1.0.0" component-emitter "1.2.1" - debug "2.3.3" - engine.io-client "1.8.3" - has-binary "0.1.7" + debug "~2.6.4" + engine.io-client "~3.1.0" + has-cors "1.1.0" indexof "0.0.1" object-component "0.0.3" + parseqs "0.0.5" parseuri "0.0.5" - socket.io-parser "2.3.1" + socket.io-parser "~3.1.1" to-array "0.1.4" -socket.io-parser@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0" +socket.io-parser@~3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.3.tgz#ed2da5ee79f10955036e3da413bfd7f1e4d86c8e" dependencies: - component-emitter "1.1.2" - debug "2.2.0" - isarray "0.0.1" - json3 "3.3.2" + component-emitter "1.2.1" + debug "~3.1.0" + has-binary2 "~1.0.2" + isarray "2.0.1" -socket.io@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b" +socket.io@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.4.tgz#c1a4590ceff87ecf13c72652f046f716b29e6014" dependencies: - debug "2.3.3" - engine.io "1.8.3" - has-binary "0.1.7" - object-assign "4.1.0" - socket.io-adapter "0.5.0" - socket.io-client "1.7.3" - socket.io-parser "2.3.1" + debug "~2.6.6" + engine.io "~3.1.0" + socket.io-adapter "~1.1.0" + socket.io-client "2.0.4" + socket.io-parser "~3.1.1" sockjs-client@1.1.4: version "1.1.4" @@ -5506,12 +7118,34 @@ sockjs-client@1.1.4: json3 "^3.3.2" url-parse "^1.1.8" -sockjs@0.3.18: - version "0.3.18" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" dependencies: faye-websocket "^0.10.0" - uuid "^2.0.2" + uuid "^3.0.1" + +socks-proxy-agent@2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3" + dependencies: + agent-base "2" + extend "3" + socks "~1.1.5" + +socks@1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691" + dependencies: + ip "^1.1.2" + smart-buffer "^1.0.4" + +socks@~1.1.5: + version "1.1.10" + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" + dependencies: + ip "^1.1.4" + smart-buffer "^1.0.13" sort-keys@^1.0.0: version "1.1.2" @@ -5527,21 +7161,33 @@ source-list-map@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" -source-map-loader@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.1.tgz#48126be9230bd47fad05e46a8c3c2e3d2dabe507" +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" dependencies: - async "^0.9.0" - loader-utils "~0.2.2" - source-map "~0.1.33" + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" -source-map-support@^0.4.0, source-map-support@^0.4.1, source-map-support@^0.4.2, source-map-support@~0.4.0: +source-map-support@^0.4.1, source-map-support@~0.4.0: version "0.4.17" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.17.tgz#6f2150553e6375375d0ccb3180502b78c18ba430" dependencies: source-map "^0.5.6" -source-map@0.1.x, source-map@~0.1.33: +source-map-support@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76" + dependencies: + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@0.1.x: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: @@ -5557,7 +7203,7 @@ source-map@^0.4.2, source-map@^0.4.4, source-map@~0.4.1: dependencies: amdefine ">=0.0.4" -source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -5598,6 +7244,12 @@ spdy@^3.4.1: select-hose "^2.0.0" spdy-transport "^2.0.18" +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + sprintf-js@^1.0.3: version "1.1.1" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" @@ -5626,6 +7278,13 @@ ssri@^5.0.0: dependencies: safe-buffer "^5.1.0" +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + "statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -5636,13 +7295,20 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stream-browserify@^2.0.1: +stream-browserify@^2.0.0, stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" dependencies: inherits "~2.0.1" readable-stream "^2.0.2" +stream-combiner2@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" + dependencies: + duplexer2 "~0.1.0" + readable-stream "^2.0.2" + stream-each@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" @@ -5650,6 +7316,16 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" +stream-http@^2.0.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.3" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + stream-http@^2.3.1: version "2.7.2" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" @@ -5664,6 +7340,22 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" +stream-splicer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.2" + +streamroller@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" + dependencies: + date-format "^1.2.0" + debug "^3.1.0" + mkdirp "^0.5.1" + readable-stream "^2.3.0" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -5687,13 +7379,13 @@ string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string_decoder@~1.0.3: +string_decoder@~1.0.0, string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: safe-buffer "~5.1.0" -stringstream@~0.0.4: +stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -5758,6 +7450,12 @@ stylus@^0.54.5: sax "0.5.x" source-map "0.1.x" +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + dependencies: + minimist "^1.1.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -5774,6 +7472,12 @@ supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0: dependencies: has-flag "^2.0.0" +supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" + dependencies: + has-flag "^3.0.0" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -5786,9 +7490,15 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -symbol-observable@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + +syntax-error@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" + dependencies: + acorn-node "^1.2.0" tapable@^0.2.7: version "0.2.8" @@ -5822,10 +7532,14 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -through@X.X.X: +"through@>=2.2.7 <3", through@X.X.X: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +thunkify@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + thunky@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e" @@ -5834,12 +7548,22 @@ time-stamp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" +timers-browserify@^1.0.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + dependencies: + process "~0.11.0" + timers-browserify@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6" dependencies: setimmediate "^1.0.4" +timespan@2.3.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929" + tmp@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" @@ -5850,7 +7574,13 @@ tmp@0.0.30: dependencies: os-tmpdir "~1.0.1" -tmp@0.0.31, tmp@0.0.x: +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +tmp@0.0.x: version "0.0.31" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" dependencies: @@ -5868,6 +7598,28 @@ to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + toposort@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.3.tgz#f02cd8a74bd8be2fc0e98611c3bacb95a171869c" @@ -5878,6 +7630,12 @@ tough-cookie@~2.3.0: dependencies: punycode "^1.4.1" +tough-cookie@~2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + tree-kill@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" @@ -5890,76 +7648,108 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -ts-node@~3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-3.2.2.tgz#bbd28e38af4aaa3e96076c466e1b220197c1a3ce" +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + +ts-node@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-4.1.0.tgz#36d9529c7b90bb993306c408cd07f7743de20712" dependencies: arrify "^1.0.0" - chalk "^2.0.0" + chalk "^2.3.0" diff "^3.1.0" make-error "^1.1.1" minimist "^1.2.0" mkdirp "^0.5.1" - source-map-support "^0.4.0" - tsconfig "^6.0.0" + source-map-support "^0.5.0" + tsconfig "^7.0.0" v8flags "^3.0.0" yn "^2.0.0" -tsconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-6.0.0.tgz#6b0e8376003d7af1864f8df8f89dd0059ffcd032" +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tsickle@^0.24.0: - version "0.24.1" - resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.24.1.tgz#039343b205bf517a333b0703978892f80a7d848e" +tsickle@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736" dependencies: minimist "^1.2.0" mkdirp "^0.5.1" - source-map "^0.5.6" - source-map-support "^0.4.2" + source-map "^0.6.0" + source-map-support "^0.5.0" tslib@^1.7.1: version "1.7.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" -tslint@~5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552" +tslib@^1.8.0, tslib@^1.8.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" + +tslint@~5.9.1: + version "5.9.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae" dependencies: babel-code-frame "^6.22.0" - colors "^1.1.2" - commander "^2.9.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" minimatch "^3.0.4" resolve "^1.3.2" semver "^5.3.0" - tslib "^1.7.1" - tsutils "^2.8.1" + tslib "^1.8.0" + tsutils "^2.12.1" + +tsscmp@~1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" -tsutils@^2.8.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.12.1.tgz#f4d95ce3391c8971e46e54c4cf0edb0a21dd5b24" +tsutils@^2.12.1: + version "2.22.2" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.22.2.tgz#0b9f3d87aa3eb95bd32d26ce2b88aa329a657951" dependencies: - tslib "^1.7.1" + tslib "^1.8.1" tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" +tty-browserify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" dependencies: safe-buffer "^5.0.1" +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + type-is@~1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" @@ -5967,15 +7757,15 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -typedarray@^0.0.6: +typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@~2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844" +typescript@~2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" -typescript@~2.6.1: +typescript@~2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" @@ -6014,9 +7804,9 @@ uglifyjs-webpack-plugin@^0.4.6: uglify-js "^2.8.29" webpack-sources "^1.0.1" -uglifyjs-webpack-plugin@~1.1.2: - version "1.1.6" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.6.tgz#f4ba8449edcf17835c18ba6ae99b9d610857fb19" +uglifyjs-webpack-plugin@^1.1.5: + version "1.2.2" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.2.tgz#e7516d4367afdb715c3847841eb46f94c45ca2b9" dependencies: cacache "^10.0.1" find-cache-dir "^1.0.0" @@ -6035,6 +7825,27 @@ ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + +umd@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" + +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -6069,10 +7880,25 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" + upper-case@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + url-loader@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7" @@ -6095,13 +7921,21 @@ url-parse@^1.1.8: querystringify "~1.0.0" requires-port "1.0.x" -url@^0.11.0: +url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: punycode "1.3.2" querystring "0.2.0" +use@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8" + dependencies: + define-property "^0.2.5" + isobject "^3.0.0" + lazy-cache "^2.0.2" + user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" @@ -6117,7 +7951,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@0.10.3, util@^0.10.3: +util@0.10.3, util@^0.10.3, util@~0.10.1: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -6135,14 +7969,22 @@ utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uuid@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" uuid@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +uuid@^3.0.1, uuid@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + +uws@~9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95" + v8flags@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.0.tgz#4be9604488e0c4123645def705b1848d16b8e01f" @@ -6160,6 +8002,10 @@ vary@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" @@ -6176,7 +8022,7 @@ vlq@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1" -vm-browserify@0.0.4: +vm-browserify@0.0.4, vm-browserify@~0.0.1: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" dependencies: @@ -6227,13 +8073,6 @@ webdriver-manager@^12.0.6: semver "^5.3.0" xml2js "^0.4.17" -webpack-concat-plugin@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/webpack-concat-plugin/-/webpack-concat-plugin-1.4.2.tgz#b60bbb626ce5001911809d6e2329fa32f4978a88" - dependencies: - md5 "^2.2.1" - uglify-js "^2.8.29" - webpack-core@^0.6.8: version "0.6.9" resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2" @@ -6241,7 +8080,17 @@ webpack-core@^0.6.8: source-list-map "~0.1.7" source-map "~0.4.1" -webpack-dev-middleware@^1.11.0, webpack-dev-middleware@~1.12.0: +webpack-dev-middleware@1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e" + dependencies: + memory-fs "~0.4.1" + mime "^1.5.0" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + time-stamp "^2.0.0" + +webpack-dev-middleware@~1.12.0: version "1.12.0" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz#d34efefb2edda7e1d3b5dbe07289513219651709" dependencies: @@ -6251,22 +8100,22 @@ webpack-dev-middleware@^1.11.0, webpack-dev-middleware@~1.12.0: range-parser "^1.0.3" time-stamp "^2.0.0" -webpack-dev-server@~2.9.3: - version "2.9.4" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.9.4.tgz#7883e61759c6a4b33e9b19ec4037bd4ab61428d1" +webpack-dev-server@~2.11.0: + version "2.11.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.2.tgz#1f4f4c78bf1895378f376815910812daf79a216f" dependencies: ansi-html "0.0.7" array-includes "^3.0.3" bonjour "^3.5.0" - chokidar "^1.6.0" + chokidar "^2.0.0" compression "^1.5.2" connect-history-api-fallback "^1.3.0" debug "^3.1.0" del "^3.0.0" - express "^4.13.3" + express "^4.16.2" html-entities "^1.2.0" http-proxy-middleware "~0.17.4" - import-local "^0.1.1" + import-local "^1.0.0" internal-ip "1.2.0" ip "^1.1.5" killable "^1.0.0" @@ -6275,13 +8124,13 @@ webpack-dev-server@~2.9.3: portfinder "^1.0.9" selfsigned "^1.9.1" serve-index "^1.7.2" - sockjs "0.3.18" + sockjs "0.3.19" sockjs-client "1.1.4" spdy "^3.4.1" - strip-ansi "^3.0.1" - supports-color "^4.2.1" - webpack-dev-middleware "^1.11.0" - yargs "^6.6.0" + strip-ansi "^3.0.0" + supports-color "^5.1.0" + webpack-dev-middleware "1.12.2" + yargs "6.6.0" webpack-merge@^4.1.0: version "4.1.0" @@ -6346,6 +8195,10 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" +when@^3.7.7: + version "3.7.8" + resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" + when@~3.6.x: version "3.6.4" resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" @@ -6386,6 +8239,10 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + worker-farm@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.2.tgz#32b312e5dc3d5d45d79ef44acc2587491cd729ae" @@ -6404,16 +8261,20 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -ws@1.1.2, ws@^1.0.1: +ws@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" dependencies: options ">=0.0.5" ultron "1.0.x" -wtf-8@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a" +ws@~3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" xhr2@^0.1.4: version "0.1.4" @@ -6441,14 +8302,18 @@ xmlbuilder@>=1.0.0, xmlbuilder@~9.0.1: version "9.0.4" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" -xmlhttprequest-ssl@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" xmlhttprequest@1: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -6485,7 +8350,7 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" -yargs@^6.6.0: +yargs@6.6.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" dependencies: @@ -6556,6 +8421,6 @@ yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" -zone.js@^0.8.14: - version "0.8.17" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.17.tgz#4c5e5185a857da8da793daf3919371c5a36b2a0b" +zone.js@^0.8.19: + version "0.8.20" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.20.tgz#a218c48db09464b19ff6fc8f0d4bb5b1046e185d" From e8d8bd770f273cd8c253a617b988839f12fc7332 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Mar 2018 11:19:52 +0100 Subject: [PATCH 147/156] Improve UI error handling - Works now with local UI - Add default ChannelDoc to each Warning and Fault channel - Remove the concept of childChannels in ThingStateChannels - Fix UI subscribe for Warning/Fault taken from generated thingstates.ts --- .../io/openems/api/channel/ReadChannel.java | 4 + .../thingstate/ThingStateChannels.java | 96 ++++++---- .../io/openems/api/controller/ThingMap.java | 128 +++++++------- edge/src/io/openems/api/device/Device.java | 12 +- edge/src/io/openems/core/ThingRepository.java | 14 +- .../system/esscluster/EssClusterNature.java | 2 - .../metercluster/MeterClusterNature.java | 5 - .../impl/protocol/modbus/ModbusElement.java | 166 +++++++++--------- .../studer/internal/object/StuderObject.java | 114 ++++++------ .../internal/property/StuderPropertyImpl.java | 66 +++---- .../device/overview/state/state.component.ts | 21 ++- 11 files changed, 336 insertions(+), 292 deletions(-) diff --git a/edge/src/io/openems/api/channel/ReadChannel.java b/edge/src/io/openems/api/channel/ReadChannel.java index 300ff3d8b91..f74b5cbf928 100644 --- a/edge/src/io/openems/api/channel/ReadChannel.java +++ b/edge/src/io/openems/api/channel/ReadChannel.java @@ -259,6 +259,10 @@ public void setChannelDoc(ChannelDoc channelDoc) throws OpenemsException { this.type = channelDoc.getTypeOpt(); } + protected Optional getChannelDocOpt() { + return channelDocOpt; + } + /** * Update value from the underlying {@link DeviceNature} and send an update event to {@link Databus}. * diff --git a/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java b/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java index 8e3a1ef4087..b1f84789f36 100644 --- a/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java +++ b/edge/src/io/openems/api/channel/thingstate/ThingStateChannels.java @@ -1,5 +1,6 @@ package io.openems.api.channel.thingstate; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -10,15 +11,18 @@ import io.openems.api.channel.ChannelChangeListener; import io.openems.api.channel.ReadChannel; import io.openems.api.channel.ThingStateChannel; +import io.openems.api.doc.ChannelDoc; +import io.openems.api.doc.ChannelInfo; import io.openems.api.exception.ConfigException; import io.openems.api.thing.Thing; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; public class ThingStateChannels extends ReadChannel implements ChannelChangeListener { private List warningChannels; private List faultChannels; - private List childChannels; private Set channelNames; public ThingStateChannels(Thing parent){ @@ -26,7 +30,6 @@ public ThingStateChannels(Thing parent){ this.warningChannels = new ArrayList<>(); this.faultChannels = new ArrayList<>(); this.channelNames = new HashSet<>(); - this.childChannels = new ArrayList<>(); updateState(); } @@ -35,6 +38,7 @@ public void addWarningChannel(ThingStateChannel channel) throws ConfigException this.warningChannels.add(channel); this.channelNames.add(channel.address()); channel.addChangeListener(this); + addDefaultChannelDoc(channel); updateState(); } else { throw new ConfigException("A channel with the name [" + channel.address() + "] is already registered!"); @@ -53,6 +57,7 @@ public void addFaultChannel(ThingStateChannel channel) throws ConfigException { this.faultChannels.add(channel); this.channelNames.add(channel.address()); channel.addChangeListener(this); + addDefaultChannelDoc(channel); updateState(); } else { throw new ConfigException("A channel with the name [" + channel.address() + "] is already registered!"); @@ -66,36 +71,73 @@ public void removeFaultChannel(ThingStateChannel channel) { updateState(); } + private void addDefaultChannelDoc(Channel channel) { + ChannelDoc channelDoc = new ChannelDoc(null, channel.id(), Optional.of(new ChannelInfo() { + + @Override + public Class annotationType() { + return ChannelInfo.class; + } + + @Override + public Role[] writeRoles() { + return ChannelInfo.DEFAULT_WRITE_ROLES.toArray(new Role[ChannelInfo.DEFAULT_WRITE_ROLES.size()]); + } + + @Override + public Class type() { + return Boolean.class; + } + + @Override + public String title() { + return ""; + } + + @Override + public Role[] readRoles() { + return ChannelInfo.DEFAULT_READ_ROLES.toArray(new Role[ChannelInfo.DEFAULT_READ_ROLES.size()]); + } + + @Override + public String description() { + return ""; + } + + @Override + public boolean isOptional() { + return false; + } + + @Override + public boolean isArray() { + return false; + } + + @Override + public String defaultValue() { + return ""; + } + })); + try { + channel.setChannelDoc(channelDoc); + } catch (OpenemsException e) { + log.error(e.getMessage()); + } + } + public List getWarningChannels() { List warningChannels = new ArrayList<>(); warningChannels.addAll(this.warningChannels); - for(ThingStateChannels child : this.childChannels) { - warningChannels.addAll(child.getWarningChannels()); - } return warningChannels; } public List getFaultChannels() { List faultChannels = new ArrayList<>(); faultChannels.addAll(this.faultChannels); - for(ThingStateChannels child : this.childChannels) { - faultChannels.addAll(child.getFaultChannels()); - } return this.faultChannels; } - public void addChildChannel(ThingStateChannels child) { - this.childChannels.add(child); - child.addChangeListener(this); - updateState(); - } - - public void removeChildChannel(ThingStateChannels child) { - child.removeChangeListener(this); - this.childChannels.add(child); - updateState(); - } - @Override public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { updateState(); @@ -103,20 +145,6 @@ public void channelChanged(Channel channel, Optional newValue, Optional ol private void updateState() { ThingState currentState = ThingState.RUN; - for(ThingStateChannels child : this.childChannels) { - if(child.isValuePresent()) { - switch(child.getValue()) { - case FAULT: - currentState = ThingState.FAULT; - case WARNING: - if(currentState != ThingState.FAULT) { - currentState = ThingState.WARNING; - } - default: - break; - } - } - } for (ThingStateChannel faultChannel : faultChannels) { if (faultChannel.isValuePresent() && faultChannel.getValue()) { currentState = ThingState.FAULT; diff --git a/edge/src/io/openems/api/controller/ThingMap.java b/edge/src/io/openems/api/controller/ThingMap.java index f770c812d05..90a15accd88 100644 --- a/edge/src/io/openems/api/controller/ThingMap.java +++ b/edge/src/io/openems/api/controller/ThingMap.java @@ -1,64 +1,64 @@ -/******************************************************************************* - * OpenEMS - Open Source Energy Management System - * Copyright (c) 2016 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.api.controller; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.thing.Thing; - -public class ThingMap { - protected final Logger log; - - private final String id; - - public ThingMap(Thing thing) { - log = LoggerFactory.getLogger(this.getClass()); - this.id = thing.id(); - } - - public String id() { - return id; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ThingMap other = (ThingMap) obj; - if (id == null) { - if (other.id != null) { - return false; - } - } else if (!id.equals(other.id)) { - return false; - } - return true; - } - -} +/******************************************************************************* + * OpenEMS - Open Source Energy Management System + * Copyright (c) 2016 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.api.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.api.thing.Thing; + +public class ThingMap { + protected final Logger log; + + private final String id; + + public ThingMap(Thing thing) { + log = LoggerFactory.getLogger(this.getClass()); + this.id = thing.id(); + } + + public String id() { + return id; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ThingMap other = (ThingMap) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return true; + } + +} diff --git a/edge/src/io/openems/api/device/Device.java b/edge/src/io/openems/api/device/Device.java index 70cff5da0f8..4fa0341b2b4 100644 --- a/edge/src/io/openems/api/device/Device.java +++ b/edge/src/io/openems/api/device/Device.java @@ -51,7 +51,6 @@ public Device(Bridge parent) throws OpenemsException { log = LoggerFactory.getLogger(this.getClass()); this.bridge = parent; this.thingState = new ThingStateChannels(this); - this.thingState.addChildChannel(this.bridge.getStateChannel()); } public Bridge getBridge() { @@ -108,16 +107,7 @@ public List getWriteTasks() { @Override public void channelChanged(Channel channel, Optional newValue, Optional oldValue) { - if (oldValue.isPresent()) { - if (oldValue.get() instanceof DeviceNature) { - this.thingState.removeChildChannel(((DeviceNature) oldValue.get()).getStateChannel()); - } - } - if (newValue.isPresent()) { - if (newValue.get() instanceof DeviceNature) { - this.thingState.addChildChannel(((DeviceNature) newValue.get()).getStateChannel()); - } - } + // nothing to do } @Override diff --git a/edge/src/io/openems/core/ThingRepository.java b/edge/src/io/openems/core/ThingRepository.java index 36314583682..6ce0297eaef 100644 --- a/edge/src/io/openems/core/ThingRepository.java +++ b/edge/src/io/openems/core/ThingRepository.java @@ -50,7 +50,9 @@ import io.openems.api.channel.Channel; import io.openems.api.channel.ConfigChannel; import io.openems.api.channel.ReadChannel; +import io.openems.api.channel.ThingStateChannel; import io.openems.api.channel.WriteChannel; +import io.openems.api.channel.thingstate.ThingStateChannels; import io.openems.api.controller.Controller; import io.openems.api.device.Device; import io.openems.api.device.nature.DeviceNature; @@ -178,6 +180,17 @@ public synchronized void addThing(Thing thing) { // It's a Method with ReturnType Channel Channel c = (Channel) ((Method) member).invoke(thing); addToChannels.accept(c); + + if(c instanceof ThingStateChannels) { + System.out.println("c is ThingStateChannels"); + ThingStateChannels tsc = (ThingStateChannels)c; + for(ThingStateChannel fc : tsc.getFaultChannels()) { + addToChannels.accept(fc); + } + for(ThingStateChannel wc : tsc.getWarningChannels()) { + addToChannels.accept(wc); + } + } } } else if (member instanceof Field) { // It's a Field with Type Channel @@ -193,7 +206,6 @@ public synchronized void addThing(Thing thing) { // Add Channel to thingChannels thingChannels.put(thing, channel.id(), channel); if (channel instanceof ConfigChannel) { - // Add Channel to configChannels thingConfigChannels.put(thing, (ConfigChannel) channel); } diff --git a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java index ddd7cb4e2e7..e210b1c26b2 100644 --- a/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java +++ b/edge/src/io/openems/impl/device/system/esscluster/EssClusterNature.java @@ -621,7 +621,6 @@ private void loadEss() { setWorkState.removeChannel(ess.setWorkState()); setActivePower.removeChannel(ess.setActivePower()); setReactivePower.removeChannel(ess.setReactivePower()); - thingState.removeChildChannel(ess.getStateChannel()); } essList.clear(); if (essIds != null && isInitialized) { @@ -644,7 +643,6 @@ private void loadEss() { setWorkState.addChannel(ess.setWorkState()); setActivePower.addChannel(ess.setActivePower()); setReactivePower.addChannel(ess.setReactivePower()); - this.thingState.addChildChannel(ess.getStateChannel()); } } } diff --git a/edge/src/io/openems/impl/device/system/metercluster/MeterClusterNature.java b/edge/src/io/openems/impl/device/system/metercluster/MeterClusterNature.java index e45deb67969..b8162a85991 100644 --- a/edge/src/io/openems/impl/device/system/metercluster/MeterClusterNature.java +++ b/edge/src/io/openems/impl/device/system/metercluster/MeterClusterNature.java @@ -386,9 +386,6 @@ private void loadMeter() { // remove old ess synchronized (symmetricMeterList) { synchronized (asymmetricMeterList) { - for(SymmetricMeterNature meter: symmetricMeterList) { - this.thingState.removeChildChannel(meter.getStateChannel()); - } symmetricMeterList.clear(); for(AsymmetricMeterNature meter: asymmetricMeterList) { this.thingState.removeChangeListener(meter.getStateChannel()); @@ -401,11 +398,9 @@ private void loadMeter() { if (nature.get() instanceof AsymmetricMeterNature) { AsymmetricMeterNature meter = (AsymmetricMeterNature) nature.get(); asymmetricMeterList.add(meter); - this.thingState.addChildChannel(meter.getStateChannel()); } else if (nature.get() instanceof SymmetricMeterNature) { SymmetricMeterNature meter = (SymmetricMeterNature) nature.get(); symmetricMeterList.add(meter); - this.thingState.addChildChannel(meter.getStateChannel()); } else { log.error("ThingID: " + id.getAsString() + " is no Meter!"); } diff --git a/edge/src/io/openems/impl/protocol/modbus/ModbusElement.java b/edge/src/io/openems/impl/protocol/modbus/ModbusElement.java index 82eaf23ce6d..1bbe720ec57 100644 --- a/edge/src/io/openems/impl/protocol/modbus/ModbusElement.java +++ b/edge/src/io/openems/impl/protocol/modbus/ModbusElement.java @@ -1,83 +1,83 @@ -/******************************************************************************* - * 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.protocol.modbus; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.channel.Channel; -import io.openems.impl.protocol.modbus.internal.range.ModbusRange; - -public abstract class ModbusElement { - protected final int address; - protected final ModbusChannel channel; - protected final Logger log; - protected ModbusRange range = null; - - public ModbusElement(int address, ModbusChannel channel) { - log = LoggerFactory.getLogger(this.getClass()); - this.address = address; - this.channel = channel; - } - - public int getAddress() { - return address; - } - - public Channel getChannel() { - return channel; - } - - public abstract int getLength(); - - public ModbusRange getModbusRange() { - return range; - } - - /** - * Set the {@link ModbusRange}, where this Element belongs to. This is called during {@link ModbusRange}.add() - * - * @param range - */ - public void setModbusRange(ModbusRange range) { - this.range = range; - } - - protected void setValue(T value) { - if (channel == null) { - return; - } else if (channel instanceof ModbusReadChannel) { - ((ModbusReadChannel) channel).updateValue(value); - } else if (channel instanceof ModbusWriteChannel) { - ((ModbusWriteChannel) channel).updateValue(value); - } else { - log.error("Unable to set value [" + value + "]. Channel [" + channel.address() - + "] is no ModbusChannel or WritableModbusChannel."); - new Throwable().printStackTrace(); - } - } - - @Override - public String toString() { - return "ModbusElement: Implementation[" + this.getClass().getSimpleName() + "], ModbusAddress[" + address + "]" - + (channel != null ? ", ChannelAddress[" + channel.address() + "]" : ""); - } -} +/******************************************************************************* + * 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.protocol.modbus; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.api.channel.Channel; +import io.openems.impl.protocol.modbus.internal.range.ModbusRange; + +public abstract class ModbusElement { + protected final int address; + protected final ModbusChannel channel; + protected final Logger log; + protected ModbusRange range = null; + + public ModbusElement(int address, ModbusChannel channel) { + log = LoggerFactory.getLogger(this.getClass()); + this.address = address; + this.channel = channel; + } + + public int getAddress() { + return address; + } + + public Channel getChannel() { + return channel; + } + + public abstract int getLength(); + + public ModbusRange getModbusRange() { + return range; + } + + /** + * Set the {@link ModbusRange}, where this Element belongs to. This is called during {@link ModbusRange}.add() + * + * @param range + */ + public void setModbusRange(ModbusRange range) { + this.range = range; + } + + protected void setValue(T value) { + if (channel == null) { + return; + } else if (channel instanceof ModbusReadChannel) { + ((ModbusReadChannel) channel).updateValue(value); + } else if (channel instanceof ModbusWriteChannel) { + ((ModbusWriteChannel) channel).updateValue(value); + } else { + log.error("Unable to set value [" + value + "]. Channel [" + channel.address() + + "] is no ModbusChannel or WritableModbusChannel."); + new Throwable().printStackTrace(); + } + } + + @Override + public String toString() { + return "ModbusElement: Implementation[" + this.getClass().getSimpleName() + "], ModbusAddress[" + address + "]" + + (channel != null ? ", ChannelAddress[" + channel.address() + "]" : ""); + } +} diff --git a/edge/src/io/openems/impl/protocol/studer/internal/object/StuderObject.java b/edge/src/io/openems/impl/protocol/studer/internal/object/StuderObject.java index 81da8d1c7ad..9d2a923b833 100644 --- a/edge/src/io/openems/impl/protocol/studer/internal/object/StuderObject.java +++ b/edge/src/io/openems/impl/protocol/studer/internal/object/StuderObject.java @@ -1,57 +1,57 @@ -/******************************************************************************* - * 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.protocol.studer.internal.object; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.thing.Thing; -import io.openems.impl.protocol.studer.internal.property.StuderProperty; - -public abstract class StuderObject { - - protected final Logger log; - - protected final int objectId; - protected final ObjectType objectType; - protected final String name; - protected final String unit; - protected final Thing parent; - - public StuderObject(int objectId, String name, String unit, Thing parent, ObjectType objectType) { - log = LoggerFactory.getLogger(this.getClass()); - this.objectId = objectId; - this.objectType = objectType; - this.name = name; - this.unit = unit; - this.parent = parent; - } - - public int getObjectId() { - return objectId; - } - - public ObjectType getObjectType() { - return objectType; - } - - public abstract StuderProperty[] getProperties(); -} +/******************************************************************************* + * 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.protocol.studer.internal.object; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.api.thing.Thing; +import io.openems.impl.protocol.studer.internal.property.StuderProperty; + +public abstract class StuderObject { + + protected final Logger log; + + protected final int objectId; + protected final ObjectType objectType; + protected final String name; + protected final String unit; + protected final Thing parent; + + public StuderObject(int objectId, String name, String unit, Thing parent, ObjectType objectType) { + log = LoggerFactory.getLogger(this.getClass()); + this.objectId = objectId; + this.objectType = objectType; + this.name = name; + this.unit = unit; + this.parent = parent; + } + + public int getObjectId() { + return objectId; + } + + public ObjectType getObjectType() { + return objectType; + } + + public abstract StuderProperty[] getProperties(); +} diff --git a/edge/src/io/openems/impl/protocol/studer/internal/property/StuderPropertyImpl.java b/edge/src/io/openems/impl/protocol/studer/internal/property/StuderPropertyImpl.java index 1eded143d0a..36ef7e7c949 100644 --- a/edge/src/io/openems/impl/protocol/studer/internal/property/StuderPropertyImpl.java +++ b/edge/src/io/openems/impl/protocol/studer/internal/property/StuderPropertyImpl.java @@ -1,33 +1,33 @@ -package io.openems.impl.protocol.studer.internal.property; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.api.thing.Thing; -import io.openems.impl.protocol.studer.StuderChannel; -import io.openems.impl.protocol.studer.internal.object.PropertyId; -import io.openems.impl.protocol.studer.internal.object.StuderObject; - -public abstract class StuderPropertyImpl { - - protected final Logger log; - - protected final StuderObject object; - protected final PropertyId propertyId; - - protected final StuderChannel channel; - - public StuderPropertyImpl(String objectName, PropertyId propertyId, StuderObject object, Thing parent) { - this.log = LoggerFactory.getLogger(this.getClass()); - this.propertyId = propertyId; - this.object = object; - String channelId = objectName + propertyId.getName(); - this.channel = initChannel(channelId, parent); - } - - protected abstract StuderChannel initChannel(String id, Thing parent); - - public StuderChannel channel() { - return this.channel; - }; -} +package io.openems.impl.protocol.studer.internal.property; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.api.thing.Thing; +import io.openems.impl.protocol.studer.StuderChannel; +import io.openems.impl.protocol.studer.internal.object.PropertyId; +import io.openems.impl.protocol.studer.internal.object.StuderObject; + +public abstract class StuderPropertyImpl { + + protected final Logger log; + + protected final StuderObject object; + protected final PropertyId propertyId; + + protected final StuderChannel channel; + + public StuderPropertyImpl(String objectName, PropertyId propertyId, StuderObject object, Thing parent) { + this.log = LoggerFactory.getLogger(this.getClass()); + this.propertyId = propertyId; + this.object = object; + String channelId = objectName + propertyId.getName(); + this.channel = initChannel(channelId, parent); + } + + protected abstract StuderChannel initChannel(String id, Thing parent); + + public StuderChannel channel() { + return this.channel; + }; +} diff --git a/ui/src/app/device/overview/state/state.component.ts b/ui/src/app/device/overview/state/state.component.ts index b1cede0bedf..15067ca7a79 100644 --- a/ui/src/app/device/overview/state/state.component.ts +++ b/ui/src/app/device/overview/state/state.component.ts @@ -60,8 +60,7 @@ export class StateComponent { newRequiredSubscribes[thingId] = this.lastRequiredSubscribes[thingId]; } else { // this is new -> generate required subscribes - // TODO - newRequiredSubscribes[thingId] = ["Fault/0", "Fault/1", "Warning/0"]; + newRequiredSubscribes[thingId] = this.getStateChannelAddresses(thingId); subscribesChanged = true; } } else { @@ -146,5 +145,23 @@ export class StateComponent { }; } + private getStateChannelAddresses(thingId: string): string[] { + let result = []; + let clazz = this.config.things[thingId].class; + if (clazz instanceof Array) { + clazz = clazz[0]; + } + if (clazz in THING_STATES) { + let meta = THING_STATES[clazz]; + for (let id in meta.faults) { + result.push("Fault/" + id); + } + for (let id in meta.warnings) { + result.push("Warning/" + id); + } + } + return result; + } + constructor(public utils: Utils) { } } From 097f996746056c6d810e0c01f5847111c5cf1bf3 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Tue, 13 Mar 2018 11:29:15 +0100 Subject: [PATCH 148/156] Remove debug output. --- edge/src/io/openems/core/ThingRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/edge/src/io/openems/core/ThingRepository.java b/edge/src/io/openems/core/ThingRepository.java index 6ce0297eaef..aa0e4495c47 100644 --- a/edge/src/io/openems/core/ThingRepository.java +++ b/edge/src/io/openems/core/ThingRepository.java @@ -182,7 +182,6 @@ public synchronized void addThing(Thing thing) { addToChannels.accept(c); if(c instanceof ThingStateChannels) { - System.out.println("c is ThingStateChannels"); ThingStateChannels tsc = (ThingStateChannels)c; for(ThingStateChannel fc : tsc.getFaultChannels()) { addToChannels.accept(fc); From ea1cd6416c9c04321850406afd32cf4c14062c15 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 20:24:24 +0100 Subject: [PATCH 149/156] Improve energytable for ess and meter --- .../energytable/energytable.component.html | 378 ++++++++----- ui/src/app/shared/device/currentdata.ts | 516 ++++++++++-------- ui/src/app/shared/service/defaulttypes.ts | 267 ++++----- 3 files changed, 696 insertions(+), 465 deletions(-) diff --git a/ui/src/app/device/overview/energytable/energytable.component.html b/ui/src/app/device/overview/energytable/energytable.component.html index fe3f1514046..4b106aed8d4 100644 --- a/ui/src/app/device/overview/energytable/energytable.component.html +++ b/ui/src/app/device/overview/energytable/energytable.component.html @@ -10,86 +10,166 @@ - - - - - General.StorageSystem - {{ config.things[thing].alias }} - - - + + + + General.StorageSystem + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        General.Soc{{ sum.soc }}%
        General.ChargePower{{ sum.chargeActivePowerAC }}W
        General.DischargePower{{ sum.dischargeActivePowerAC }}W
        General.ChargePowerL1{{ sum.chargeActivePowerACL1 }}W
        L2{{ sum.chargeActivePowerACL2 }}W
        General.Soc{{ data.Soc }}%L3{{ sum.chargeActivePowerACL3 }}W
        General.ChargePower{{ data.ActivePower | sign }}0W
        General.DischargePower{{ data.ActivePower }}0W
        General.ChargePowerL1{{ data.ActivePowerL1 | sign }}0W
        L2{{ data.ActivePowerL2 | sign }}0W
        General.DischargePowerL1{{ sum.dischargeActivePowerACL1 }}W
        L2{{ sum.dischargeActivePowerACL2 }}W
        L3{{ sum.dischargeActivePowerACL3 }}W
        Beladung DC{{ sum.chargeActivePowerDC }}W
        +
        +
        + + + + + + + {{ config.things[thing].alias }} + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - -
        L3{{ data.ActivePowerL3 | sign }}0W
        General.DischargePowerL1{{ data.ActivePowerL1 }}0W
        L2{{ data.ActivePowerL2 }}0W
        L3{{ data.ActivePowerL3 }}0WGeneral.Soc{{ data.Soc }}%
        -
        -
        + + + + General.ChargePower + + {{ data.ActivePower | sign }} + 0 + W + + + General.DischargePower + + {{ data.ActivePower }} + 0 + W + + + + + + General.ChargePower + L1 + {{ data.ActivePowerL1 | sign }} + 0 + W + + + + L2 + {{ data.ActivePowerL2 | sign }} + 0 + W + + + + L3 + {{ data.ActivePowerL3 | sign }} + 0 + W + + + General.DischargePower + L1 + {{ data.ActivePowerL1 }} + 0 + W + + + + L2 + {{ data.ActivePowerL2 }} + 0 + W + + + + L3 + {{ data.ActivePowerL3 }} + 0 + W + + + + + +
        @@ -170,54 +250,104 @@
        - - - - - Device.Overview.Energymonitor.ProductionMeter - {{ config.things[thing].alias }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        General.Production{{ data.ActualPower }}W
        General.Production{{ data.ActivePower }}W
        General.ProductionL1{{ data.ActivePowerL1 }}W
        L2{{ data.ActivePowerL2 }}W
        L3{{ data.ActivePowerL3 }}W
        -
        -
        + + + + Device.Overview.Energymonitor.ProductionMeter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        General.Production{{ sum.activePowerAC }}W
        General.ProductionL1{{ sum.activePowerACL1 }}W
        General.ProductionL2{{ sum.activePowerACL2 }}W
        General.ProductionL3{{ sum.activePowerACL3 }}W
        Erzeugung DC{{ sum.activePowerDC }}W
        +
        +
        + + + + + + + {{ config.things[thing].alias }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        General.Production{{ data.ActualPower }}W
        General.Production{{ data.ActivePower }}W
        General.ProductionL1{{ data.ActivePowerL1 }}W
        L2{{ data.ActivePowerL2 }}W
        L3{{ data.ActivePowerL3 }}W
        +
        +
        +
        diff --git a/ui/src/app/shared/device/currentdata.ts b/ui/src/app/shared/device/currentdata.ts index 6447d9e67e0..f3a3185d779 100644 --- a/ui/src/app/shared/device/currentdata.ts +++ b/ui/src/app/shared/device/currentdata.ts @@ -1,214 +1,304 @@ -import { DefaultTypes } from '../service/defaulttypes'; -import { ConfigImpl } from './config'; -import { Utils } from '../service/utils'; - -export class CurrentDataAndSummary { - public readonly summary: DefaultTypes.Summary; - - constructor(public data: DefaultTypes.Data, config: ConfigImpl) { - this.summary = this.calculateSummary(data, config); - } - - private calculateSummary(currentData: DefaultTypes.Data, config: ConfigImpl): DefaultTypes.Summary { - let result: DefaultTypes.Summary = { - storage: { - soc: null, - chargeActivePower: null, // sum of chargeActivePowerAC and chargeActivePowerDC - chargeActivePowerAC: null, - chargeActivePowerDC: null, - maxChargeActivePower: null, - dischargeActivePower: null, // sum of dischargeActivePowerAC and dischargeActivePowerDC - dischargeActivePowerAC: null, - dischargeActivePowerDC: null, - maxDischargeActivePower: null - }, production: { - powerRatio: null, - activePower: null, // sum of activePowerAC and activePowerDC - activePowerAC: null, - activePowerDC: null, - maxActivePower: null - }, grid: { - powerRatio: null, - buyActivePower: null, - maxBuyActivePower: null, - sellActivePower: null, - maxSellActivePower: null - }, consumption: { - powerRatio: null, - activePower: null - } - }; - - { - /* - * Storage - * > 0 => Discharge - * < 0 => Charge - */ - let soc = null; - let activePowerAC = null; - let activePowerDC = null; - let countSoc = 0; - for (let thing of config.storageThings) { - if (thing in currentData) { - let essData = currentData[thing]; - if ("Soc" in essData) { - soc = Utils.addSafely(soc, essData.Soc); - countSoc += 1; - } - activePowerAC = Utils.addSafely(activePowerAC, this.getActivePower(essData)); - } - } - for (let thing of config.chargers) { - if (thing in currentData) { - let essData = currentData[thing]; - activePowerDC = Utils.subtractSafely(activePowerDC, essData.ActualPower); - } - } - result.storage.soc = Utils.divideSafely(soc, countSoc); - if(result.storage.soc > 100 || result.storage.soc < 0) { - console.log("SOC", result.storage.soc); - result.storage.soc = null; - } - if (activePowerAC != null) { - if (activePowerAC > 0) { - result.storage.chargeActivePowerAC = 0; - result.storage.dischargeActivePowerAC = activePowerAC; - } else { - result.storage.chargeActivePowerAC = activePowerAC * -1; - result.storage.dischargeActivePowerAC = 0; - } - } - if (activePowerDC != null) { - if (activePowerDC > 0) { - result.storage.chargeActivePowerDC = activePowerDC; - result.storage.dischargeActivePowerDC = 0; - } else { - result.storage.chargeActivePowerDC = 0; - result.storage.dischargeActivePowerDC = activePowerDC * -1; - } - } - let activePower = Utils.subtractSafely( - Utils.addSafely(result.storage.chargeActivePowerAC, result.storage.chargeActivePowerDC), - Utils.addSafely(result.storage.dischargeActivePowerAC, result.storage.dischargeActivePowerDC)); - if (activePower != null) { - if (activePower > 0) { - result.storage.chargeActivePower = activePower; - result.storage.dischargeActivePower = 0; - } else { - result.storage.chargeActivePower = 0; - result.storage.dischargeActivePower = activePower * -1; - } - } - } - - { - /* - * Grid - * > 0 => Buy from grid - * < 0 => Sell to grid - */ - let activePower = null; - let ratio = 0; - let maxSell = 0; - let maxBuy = 0; - for (let thing of config.gridMeters) { - let meterData = currentData[thing]; - let meterConfig = config.things[thing]; - activePower = Utils.addSafely(activePower, this.getActivePower(meterData)); - if ("maxActivePower" in meterConfig) { - maxBuy += meterConfig.maxActivePower; - } - if ("minActivePower" in meterConfig) { - maxSell += meterConfig.minActivePower; - } - } - // set GridBuy and GridSell - result.grid.maxSellActivePower = maxSell * -1; - result.grid.maxBuyActivePower = maxBuy; - if (activePower != null) { - if (activePower > 0) { - result.grid.sellActivePower = 0; - result.grid.buyActivePower = activePower; - ratio = result.grid.buyActivePower / maxSell * 100; - } else { - result.grid.sellActivePower = activePower * -1; - result.grid.buyActivePower = 0; - ratio = result.grid.sellActivePower / maxSell * -100; - } - } - result.grid.powerRatio = ratio; - } - - { - /* - * Production - */ - let powerRatio = 0; - let activePowerAC = null; - let activePowerDC = null; - let maxActivePower = 0; - for (let thing of config.productionMeters) { - let meterData = currentData[thing]; - let meterConfig = config.things[thing]; - activePowerAC = Utils.addSafely(activePowerAC, this.getActivePower(meterData)); - if ("ActualPower" in meterData && meterData.ActualPower != null) { - activePowerDC = Utils.addSafely(activePowerDC, meterData.ActualPower); - } - if ("maxActivePower" in meterConfig) { - maxActivePower += meterConfig.maxActivePower; - } - if ("maxActualPower" in meterConfig) { - maxActivePower += meterConfig.maxActualPower; - } - } - - // correct negative production - if (activePowerAC < 0) { - console.warn("negative production? ", config.productionMeters, activePowerAC) - // TODO activePowerAC = 0; - } - if (maxActivePower < 0) { maxActivePower = 0; } - - if (maxActivePower == 0) { - powerRatio = 100; - } else { - let activePowerACDC = Utils.addSafely(activePowerAC, activePowerDC); - powerRatio = Utils.divideSafely(activePowerACDC, (maxActivePower / 100)); - } - - result.production.powerRatio = powerRatio; - result.production.activePowerAC = activePowerAC; - result.production.activePowerDC = activePowerDC; - result.production.activePower = Utils.addSafely(activePowerAC, activePowerDC); - result.production.maxActivePower = maxActivePower; - } - - { - /* - * Consumption - */ - // Consumption = GridBuy + Production + ESS-Discharge - GridSell - ESS-Charge - let minus = Utils.addSafely(result.grid.sellActivePower, result.storage.chargeActivePowerAC); - let plus = Utils.addSafely(Utils.addSafely(result.grid.buyActivePower, result.production.activePowerAC), result.storage.dischargeActivePowerAC); - let activePower = Utils.subtractSafely(plus, minus); - - let maxActivePower = result.grid.maxBuyActivePower - result.grid.maxSellActivePower // - + result.production.maxActivePower // - + result.storage.maxChargeActivePower - result.storage.maxDischargeActivePower; - result.consumption.powerRatio = Utils.divideSafely(activePower, (maxActivePower / 100)); - result.consumption.activePower = activePower; - } - return result; - } - - private getActivePower(o: any): number { - if ("ActivePowerL1" in o && o.ActivePowerL1 != null && "ActivePowerL2" in o && o.ActivePowerL2 != null && "ActivePowerL3" in o && o.ActivePowerL3 != null) { - return o.ActivePowerL1 + o.ActivePowerL2 + o.ActivePowerL3; - } else if ("ActivePower" in o && o.ActivePower != null) { - return o.ActivePower; - } else { - return null; - } - } +import { DefaultTypes } from '../service/defaulttypes'; +import { ConfigImpl } from './config'; +import { Utils } from '../service/utils'; + +export class CurrentDataAndSummary { + public readonly summary: DefaultTypes.Summary; + + constructor(public data: DefaultTypes.Data, config: ConfigImpl) { + this.summary = this.calculateSummary(data, config); + } + + private calculateSummary(currentData: DefaultTypes.Data, config: ConfigImpl): DefaultTypes.Summary { + let result: DefaultTypes.Summary = { + storage: { + soc: null, + asymmetric: false, + chargeActivePower: null, // sum of chargeActivePowerAC and chargeActivePowerDC + chargeActivePowerAC: null, + chargeActivePowerACL1: null, + chargeActivePowerACL2: null, + chargeActivePowerACL3: null, + chargeActivePowerDC: null, + maxChargeActivePower: null, + dischargeActivePower: null, // sum of dischargeActivePowerAC and dischargeActivePowerDC + dischargeActivePowerAC: null, + dischargeActivePowerACL1: null, + dischargeActivePowerACL2: null, + dischargeActivePowerACL3: null, + dischargeActivePowerDC: null, + maxDischargeActivePower: null + }, production: { + asymmetric: false, + powerRatio: null, + activePower: null, // sum of activePowerAC and activePowerDC + activePowerAC: null, + activePowerACL1: null, + activePowerACL2: null, + activePowerACL3: null, + activePowerDC: null, + maxActivePower: null + }, grid: { + powerRatio: null, + buyActivePower: null, + maxBuyActivePower: null, + sellActivePower: null, + maxSellActivePower: null + }, consumption: { + powerRatio: null, + activePower: null + } + }; + + { + /* + * Storage + * > 0 => Discharge + * < 0 => Charge + */ + let soc = null; + let asymmetric = false; + let activePowerAC = null; + let activePowerACL1 = null; + let activePowerACL2 = null; + let activePowerACL3 = null; + let activePowerDC = null; + let countSoc = 0; + for (let thing of config.storageThings) { + if (thing in currentData) { + let essData = currentData[thing]; + if ("Soc" in essData) { + soc = Utils.addSafely(soc, essData.Soc); + countSoc += 1; + } + let thisActivePowerAC = this.getActivePower(essData); + let thisActivePowerACL = Utils.divideSafely(thisActivePowerAC, 3); + activePowerAC = Utils.addSafely(activePowerAC, thisActivePowerAC); + if ("ActivePowerL1" in essData) { + asymmetric = true; + activePowerACL1 = Utils.addSafely(activePowerACL1, essData.ActivePowerL1); + } else { + activePowerACL1 = Utils.addSafely(activePowerACL1, thisActivePowerACL); + } + if ("ActivePowerL2" in essData) { + asymmetric = true; + activePowerACL2 = Utils.addSafely(activePowerACL2, essData.ActivePowerL2); + } else { + activePowerACL2 = Utils.addSafely(activePowerACL2, thisActivePowerACL); + } + if ("ActivePowerL3" in essData) { + asymmetric = true; + activePowerACL3 = Utils.addSafely(activePowerACL3, essData.ActivePowerL3); + } else { + activePowerACL3 = Utils.addSafely(activePowerACL3, thisActivePowerACL); + } + } + } + for (let thing of config.chargers) { + if (thing in currentData) { + let essData = currentData[thing]; + activePowerDC = Utils.subtractSafely(activePowerDC, essData.ActualPower); + } + } + result.storage.soc = Utils.divideSafely(soc, countSoc); + result.storage.asymmetric = asymmetric; + if (result.storage.soc > 100 || result.storage.soc < 0) { + result.storage.soc = null; + } + if (activePowerAC != null) { + if (activePowerAC > 0) { + result.storage.chargeActivePowerAC = 0; + result.storage.dischargeActivePowerAC = activePowerAC; + } else { + result.storage.chargeActivePowerAC = activePowerAC * -1; + result.storage.dischargeActivePowerAC = 0; + } + } + if (activePowerACL1 != null) { + if (activePowerACL1 > 0) { + result.storage.chargeActivePowerACL1 = 0; + result.storage.dischargeActivePowerACL1 = activePowerACL1; + } else { + result.storage.chargeActivePowerACL1 = activePowerACL1 * -1; + result.storage.dischargeActivePowerACL1 = 0; + } + } + if (activePowerACL2 != null) { + if (activePowerACL2 > 0) { + result.storage.chargeActivePowerACL2 = 0; + result.storage.dischargeActivePowerACL2 = activePowerACL2; + } else { + result.storage.chargeActivePowerACL2 = activePowerACL2 * -1; + result.storage.dischargeActivePowerACL2 = 0; + } + } + if (activePowerACL3 != null) { + if (activePowerACL3 > 0) { + result.storage.chargeActivePowerACL3 = 0; + result.storage.dischargeActivePowerACL3 = activePowerACL3; + } else { + result.storage.chargeActivePowerACL3 = activePowerACL3 * -1; + result.storage.dischargeActivePowerACL3 = 0; + } + } + if (activePowerDC != null) { + if (activePowerDC > 0) { + result.storage.chargeActivePowerDC = activePowerDC; + result.storage.dischargeActivePowerDC = 0; + } else { + result.storage.chargeActivePowerDC = 0; + result.storage.dischargeActivePowerDC = activePowerDC * -1; + } + } + let activePower = Utils.subtractSafely( + Utils.addSafely(result.storage.chargeActivePowerAC, result.storage.chargeActivePowerDC), + Utils.addSafely(result.storage.dischargeActivePowerAC, result.storage.dischargeActivePowerDC)); + if (activePower != null) { + if (activePower > 0) { + result.storage.chargeActivePower = activePower; + result.storage.dischargeActivePower = 0; + } else { + result.storage.chargeActivePower = 0; + result.storage.dischargeActivePower = activePower * -1; + } + } + } + + { + /* + * Grid + * > 0 => Buy from grid + * < 0 => Sell to grid + */ + let activePower = null; + let ratio = 0; + let maxSell = 0; + let maxBuy = 0; + for (let thing of config.gridMeters) { + let meterData = currentData[thing]; + let meterConfig = config.things[thing]; + activePower = Utils.addSafely(activePower, this.getActivePower(meterData)); + if ("maxActivePower" in meterConfig) { + maxBuy += meterConfig.maxActivePower; + } + if ("minActivePower" in meterConfig) { + maxSell += meterConfig.minActivePower; + } + } + // set GridBuy and GridSell + result.grid.maxSellActivePower = maxSell * -1; + result.grid.maxBuyActivePower = maxBuy; + if (activePower != null) { + if (activePower > 0) { + result.grid.sellActivePower = 0; + result.grid.buyActivePower = activePower; + ratio = result.grid.buyActivePower / maxSell * 100; + } else { + result.grid.sellActivePower = activePower * -1; + result.grid.buyActivePower = 0; + ratio = result.grid.sellActivePower / maxSell * -100; + } + } + result.grid.powerRatio = ratio; + } + + { + /* + * Production + */ + let powerRatio = 0; + let asymmetric = false; + let activePowerAC = null; + let activePowerACL1 = null; + let activePowerACL2 = null; + let activePowerACL3 = null; + let activePowerDC = null; + let maxActivePower = 0; + for (let thing of config.productionMeters) { + let meterData = currentData[thing]; + let meterConfig = config.things[thing]; + let thisActivePowerAC = this.getActivePower(meterData); + let thisActivePowerACL = Utils.divideSafely(thisActivePowerAC, 3); + activePowerAC = Utils.addSafely(activePowerAC, thisActivePowerAC); + if ("ActivePowerL1" in meterData) { + asymmetric = true; + activePowerACL1 = Utils.addSafely(activePowerACL1, meterData.ActivePowerL1); + } else { + activePowerACL1 = Utils.addSafely(activePowerACL1, thisActivePowerACL); + } + if ("ActivePowerL2" in meterData) { + asymmetric = true; + activePowerACL2 = Utils.addSafely(activePowerACL2, meterData.ActivePowerL2); + } else { + activePowerACL2 = Utils.addSafely(activePowerACL2, thisActivePowerACL); + } + if ("ActivePowerL3" in meterData) { + asymmetric = true; + activePowerACL3 = Utils.addSafely(activePowerACL3, meterData.ActivePowerL3); + } else { + activePowerACL3 = Utils.addSafely(activePowerACL3, thisActivePowerACL); + } + if ("ActualPower" in meterData && meterData.ActualPower != null) { + activePowerDC = Utils.addSafely(activePowerDC, meterData.ActualPower); + } + if ("maxActivePower" in meterConfig) { + maxActivePower += meterConfig.maxActivePower; + } + if ("maxActualPower" in meterConfig) { + maxActivePower += meterConfig.maxActualPower; + } + } + + // correct negative production + if (activePowerAC < 0) { + console.warn("negative production? ", config.productionMeters, activePowerAC) + // TODO activePowerAC = 0; + } + if (maxActivePower < 0) { maxActivePower = 0; } + + if (maxActivePower == 0) { + powerRatio = 100; + } else { + let activePowerACDC = Utils.addSafely(activePowerAC, activePowerDC); + powerRatio = Utils.divideSafely(activePowerACDC, (maxActivePower / 100)); + } + + result.production.powerRatio = powerRatio; + result.production.asymmetric = asymmetric; + result.production.activePowerAC = activePowerAC; + result.production.activePowerACL1 = activePowerACL1; + result.production.activePowerACL2 = activePowerACL2; + result.production.activePowerACL3 = activePowerACL3; + result.production.activePowerDC = activePowerDC; + result.production.activePower = Utils.addSafely(activePowerAC, activePowerDC); + result.production.maxActivePower = maxActivePower; + } + + { + /* + * Consumption + */ + // Consumption = GridBuy + Production + ESS-Discharge - GridSell - ESS-Charge + let minus = Utils.addSafely(result.grid.sellActivePower, result.storage.chargeActivePowerAC); + let plus = Utils.addSafely(Utils.addSafely(result.grid.buyActivePower, result.production.activePowerAC), result.storage.dischargeActivePowerAC); + let activePower = Utils.subtractSafely(plus, minus); + + let maxActivePower = result.grid.maxBuyActivePower - result.grid.maxSellActivePower // + + result.production.maxActivePower // + + result.storage.maxChargeActivePower - result.storage.maxDischargeActivePower; + result.consumption.powerRatio = Utils.divideSafely(activePower, (maxActivePower / 100)); + result.consumption.activePower = activePower; + } + return result; + } + + private getActivePower(o: any): number { + if ("ActivePowerL1" in o && o.ActivePowerL1 != null && "ActivePowerL2" in o && o.ActivePowerL2 != null && "ActivePowerL3" in o && o.ActivePowerL3 != null) { + return o.ActivePowerL1 + o.ActivePowerL2 + o.ActivePowerL3; + } else if ("ActivePower" in o && o.ActivePower != null) { + return o.ActivePower; + } else { + return null; + } + } } \ No newline at end of file diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index 9243928ec15..2fb011ddb93 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -1,129 +1,140 @@ -import { Role } from '../type/role' -import { UUID } from 'angular2-uuid'; - -export module DefaultTypes { - - export type Backend = "OpenEMS Backend" | "OpenEMS Edge"; - - export type ConnectionStatus = "online" | "connecting" | "waiting for authentication" | "failed"; - - export interface ChannelAddresses { - [thing: string]: string[]; - } - - export interface ThingConfig { - id: string, - class: string | string[], - [channel: string]: any - } - - export interface Config { - things: { - [id: string]: ThingConfig - }, - meta: { - [clazz: string]: { - implements: string[], - channels: { - [channel: string]: { - name: string, - title: string, - type: string | string[], - optional: boolean, - array: boolean, - readRoles: Role[], - writeRoles: Role[] - } - } - } - } - } - - export interface Data { - [thing: string]: { - [channel: string]: any - } - } - - export interface HistoricData { - data: [{ - time: string, - channels: Data - }] - } - - export interface Summary { - storage: { - soc: number, - chargeActivePower: number, - chargeActivePowerAC: number, - chargeActivePowerDC: number, - maxChargeActivePower: number, - dischargeActivePower: number, - dischargeActivePowerAC: number, - dischargeActivePowerDC: number, - maxDischargeActivePower: number - }, production: { - powerRatio: number, - activePower: number, // sum of activePowerAC and activePowerDC - activePowerAC: number, - activePowerDC: number, - maxActivePower: number - }, grid: { - powerRatio: number, - buyActivePower: number, - maxBuyActivePower: number, - sellActivePower: number, - maxSellActivePower: number - }, consumption: { - powerRatio: number, - activePower: number - } - } - - export interface MessageMetadataDevice { - id: number, - name: string, - comment: string, - producttype: string, - role: string, - online: boolean - } - - export type NotificationType = "success" | "error" | "warning" | "info"; - - export interface Notification { - type: NotificationType; - message: string; - code?: number, - params?: string[] - } - - export interface Log { - time: number | string, - level: string, - source: string, - message: string, - color?: string /* is added later */ - } - - export type LanguageTag = "de" | "en" | "cz" | "nl"; - - export interface IdentifiedMessage { - messageId: { - ui: string, - backend?: string - }, - edgeId?: number, - [thing: string]: {} - } - - export interface ConfigUpdate extends IdentifiedMessage { - config: { - mode: "update", - thing: string, - channel: string, - value: any - } - } +import { Role } from '../type/role' +import { UUID } from 'angular2-uuid'; + +export module DefaultTypes { + + export type Backend = "OpenEMS Backend" | "OpenEMS Edge"; + + export type ConnectionStatus = "online" | "connecting" | "waiting for authentication" | "failed"; + + export interface ChannelAddresses { + [thing: string]: string[]; + } + + export interface ThingConfig { + id: string, + class: string | string[], + [channel: string]: any + } + + export interface Config { + things: { + [id: string]: ThingConfig + }, + meta: { + [clazz: string]: { + implements: string[], + channels: { + [channel: string]: { + name: string, + title: string, + type: string | string[], + optional: boolean, + array: boolean, + readRoles: Role[], + writeRoles: Role[] + } + } + } + } + } + + export interface Data { + [thing: string]: { + [channel: string]: any + } + } + + export interface HistoricData { + data: [{ + time: string, + channels: Data + }] + } + + export interface Summary { + storage: { + soc: number, + asymmetric: boolean + chargeActivePower: number, + chargeActivePowerAC: number, + chargeActivePowerACL1: number, + chargeActivePowerACL2: number, + chargeActivePowerACL3: number, + chargeActivePowerDC: number, + maxChargeActivePower: number, + dischargeActivePower: number, + dischargeActivePowerAC: number, + dischargeActivePowerACL1: number, + dischargeActivePowerACL2: number, + dischargeActivePowerACL3: number, + dischargeActivePowerDC: number, + maxDischargeActivePower: number + }, production: { + powerRatio: number, + asymmetric: boolean, + activePower: number, // sum of activePowerAC and activePowerDC + activePowerAC: number, + activePowerACL1: number, + activePowerACL2: number, + activePowerACL3: number, + activePowerDC: number, + maxActivePower: number + }, grid: { + powerRatio: number, + buyActivePower: number, + maxBuyActivePower: number, + sellActivePower: number, + maxSellActivePower: number + }, consumption: { + powerRatio: number, + activePower: number + } + } + + export interface MessageMetadataDevice { + id: number, + name: string, + comment: string, + producttype: string, + role: string, + online: boolean + } + + export type NotificationType = "success" | "error" | "warning" | "info"; + + export interface Notification { + type: NotificationType; + message: string; + code?: number, + params?: string[] + } + + export interface Log { + time: number | string, + level: string, + source: string, + message: string, + color?: string /* is added later */ + } + + export type LanguageTag = "de" | "en" | "cz" | "nl"; + + export interface IdentifiedMessage { + messageId: { + ui: string, + backend?: string + }, + edgeId?: number, + [thing: string]: {} + } + + export interface ConfigUpdate extends IdentifiedMessage { + config: { + mode: "update", + thing: string, + channel: string, + value: any + } + } } \ No newline at end of file From ed64e0429f44384e5778a7408565f579f4386130 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 20:26:06 +0100 Subject: [PATCH 150/156] Reset UI environment files --- ui/src/environments/environment.ts | 28 +++++++++++++------------- ui/src/environments/openems-backend.ts | 22 ++++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ui/src/environments/environment.ts b/ui/src/environments/environment.ts index bdde120aad8..ea8fbf638d1 100644 --- a/ui/src/environments/environment.ts +++ b/ui/src/environments/environment.ts @@ -1,15 +1,15 @@ -import { Environment } from "../app/shared/type/environment"; -import { DefaultTypes } from "../app/shared/service/defaulttypes"; - -class DefaultEnvironment extends Environment { - public readonly production = false; - // For OpenEMS Edge - // public readonly url = "ws://" + location.hostname + ":8085"; - // public readonly backend: DefaultTypes.Backend = "OpenEMS Edge"; - // For OpenEMS Backend - public readonly url = "ws://" + location.hostname + ":8078"; - public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; - public debugMode = true; -} - +import { Environment } from "../app/shared/type/environment"; +import { DefaultTypes } from "../app/shared/service/defaulttypes"; + +class DefaultEnvironment extends Environment { + public readonly production = false; + public debugMode = true; + // For OpenEMS Edge + public readonly url = "ws://" + location.hostname + ":8085"; + public readonly backend: DefaultTypes.Backend = "OpenEMS Edge"; + // For OpenEMS Backend + // public readonly url = "ws://" + location.hostname + ":8078"; + // public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; +} + export const environment = new DefaultEnvironment(); \ No newline at end of file diff --git a/ui/src/environments/openems-backend.ts b/ui/src/environments/openems-backend.ts index 4789fa588c5..2ea5724ae27 100644 --- a/ui/src/environments/openems-backend.ts +++ b/ui/src/environments/openems-backend.ts @@ -1,11 +1,11 @@ -import { Environment } from "../app/shared/type/environment"; -import { DefaultTypes } from '../app/shared/service/defaulttypes'; - -class OpenemsBackendEnvironment extends Environment { - public readonly production = true; - public readonly url = (location.protocol == "https:" ? "wss" : "ws") + - "://" + location.hostname + ":" + location.port + "/openems-backend-ui"; - public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; -} - -export const environment = new OpenemsBackendEnvironment(); +import { Environment } from "../app/shared/type/environment"; +import { DefaultTypes } from '../app/shared/service/defaulttypes'; + +class OpenemsBackendEnvironment extends Environment { + public readonly production = true; + public readonly url = (location.protocol == "https:" ? "wss" : "ws") + + "://" + location.hostname + ":" + location.port + "/openems-backend-ui"; + public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; +} + +export const environment = new OpenemsBackendEnvironment(); From 95306d8efa274bc87ad4d41a988f6c2e0136ddea Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 20:27:28 +0100 Subject: [PATCH 151/156] Prepare for release: new temporary url for FeneconPersistence --- .../io/openems/impl/persistence/fenecon/FeneconPersistence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java index ed52ba4b2df..bd31a25a150 100644 --- a/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java +++ b/edge/src/io/openems/impl/persistence/fenecon/FeneconPersistence.java @@ -79,7 +79,7 @@ public class FeneconPersistence extends Persistence implements ChannelChangeList Role.ADMIN }) public final ConfigChannel apikey = new ConfigChannel("apikey", this).doNotPersist(); - @ChannelInfo(title = "Uri", description = "Sets the connection Uri to FENECON Cloud.", type = String.class, defaultValue = "\"wss://fenecon.de:443/openems-backend\"") + @ChannelInfo(title = "Uri", description = "Sets the connection Uri to FENECON Cloud.", type = String.class, defaultValue = "\"wss://fenecon.de:443/openems-backend2\"") public final ConfigChannel uri = new ConfigChannel("uri", this).doNotPersist(); @ChannelInfo(title = "Sets the duration of each cycle in milliseconds", type = Integer.class) From bfa495ebfd45b73e40466942d51a09f6d4bc6880 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 20:30:15 +0100 Subject: [PATCH 152/156] Push version to 2018.3 --- edge/pom.xml | 2 +- ui/package-lock.json | 30258 ++++++++++++------------ ui/package.json | 164 +- ui/pom.xml | 2 +- ui/src/app/about/about.component.html | 6 +- 5 files changed, 15216 insertions(+), 15216 deletions(-) diff --git a/edge/pom.xml b/edge/pom.xml index b82c28c4bb5..3c37211cde3 100644 --- a/edge/pom.xml +++ b/edge/pom.xml @@ -5,7 +5,7 @@ http://openems.io io.openems edge - 2018.2.0 + 2018.3.0 jar https://github.com/OpenEMS/openems diff --git a/ui/package-lock.json b/ui/package-lock.json index db28589b12a..ae3d174b9ee 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,15129 +1,15129 @@ -{ - "name": "openems-ui", - "version": "2018.2.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular-devkit/build-optimizer": { - "version": "0.0.42", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.42.tgz", - "integrity": "sha512-BAYCVZ10ro6mgZQDZiNiVbX8ppygw4q7z/stpwG8WjMswgMRIcxsxYoC1VFuWcUPAf4UyfTIav6e8UZWA5+xnQ==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "source-map": "0.5.7", - "typescript": "2.6.2", - "webpack-sources": "1.1.0" - }, - "dependencies": { - "typescript": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", - "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", - "dev": true - } - } - }, - "@angular-devkit/core": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.29.tgz", - "integrity": "sha512-jtUBA0pIrkdXcVqDmDrGlniqwM7NFOKdo7vWFDmCVLBbC9rZHeYW5Xv/+4HyBhGLJ4wxsAkUjsHKWGJINPPpiw==", - "dev": true, - "requires": { - "ajv": "5.5.2", - "chokidar": "1.7.0", - "rxjs": "5.5.6", - "source-map": "0.5.7" - } - }, - "@angular-devkit/schematics": { - "version": "0.0.52", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.52.tgz", - "integrity": "sha512-NtG8VB5aWtg0cw1Y7EJinJMuAnXsNdkQkkVe/i7CO6TPLyFQSFQCN1YojCr43l8jTWTRebRslrBawPCMOxsOgw==", - "dev": true, - "requires": { - "@ngtools/json-schema": "1.1.0", - "rxjs": "5.5.6" - } - }, - "@angular/animations": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.2.5.tgz", - "integrity": "sha512-70ElCmaeDxLQc2OkgYhJjXj4zjtdjI4K1D5ZZm/uSPLlUcqC6uf6skCXlhMawQoPbsL/SXE5xw2HlMgEbhUysw==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/cdk": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.2.1.tgz", - "integrity": "sha512-8vsHeRymM+p82JeBzanrjmxp0koTU5W8cXO05ojECRsj6gUE/C950rMfFDga7fC8Pu5KTru/hWQoOcKErb3Uzg==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/cli": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.6.7.tgz", - "integrity": "sha512-TprSjnQrEdrTsCAB5K/lCLuXZUH/y+l/BAR0aZLpubpZP8Ldgmq7q56trxL5wNSs3o6A8Vh43ZKNYOuKtnzlXQ==", - "dev": true, - "requires": { - "@angular-devkit/build-optimizer": "0.0.42", - "@angular-devkit/core": "0.0.29", - "@angular-devkit/schematics": "0.0.52", - "@ngtools/json-schema": "1.1.0", - "@ngtools/webpack": "1.9.7", - "@schematics/angular": "0.1.17", - "autoprefixer": "7.2.6", - "chalk": "2.2.2", - "circular-dependency-plugin": "4.4.0", - "common-tags": "1.7.2", - "copy-webpack-plugin": "4.4.1", - "core-object": "3.1.5", - "css-loader": "0.28.9", - "cssnano": "3.10.0", - "denodeify": "1.2.1", - "ember-cli-string-utils": "1.1.0", - "exports-loader": "0.6.4", - "extract-text-webpack-plugin": "3.0.2", - "file-loader": "1.1.6", - "fs-extra": "4.0.3", - "glob": "7.1.2", - "html-webpack-plugin": "2.30.1", - "istanbul-instrumenter-loader": "3.0.0", - "karma-source-map-support": "1.2.0", - "less": "2.7.3", - "less-loader": "4.0.5", - "license-webpack-plugin": "1.1.1", - "loader-utils": "1.1.0", - "lodash": "4.17.5", - "memory-fs": "0.4.1", - "minimatch": "3.0.4", - "node-modules-path": "1.0.1", - "node-sass": "4.7.2", - "nopt": "4.0.1", - "opn": "5.1.0", - "portfinder": "1.0.13", - "postcss-import": "11.1.0", - "postcss-loader": "2.1.0", - "postcss-url": "7.3.0", - "raw-loader": "0.5.1", - "resolve": "1.5.0", - "rxjs": "5.5.6", - "sass-loader": "6.0.6", - "semver": "5.5.0", - "silent-error": "1.1.0", - "source-map-support": "0.4.18", - "style-loader": "0.13.2", - "stylus": "0.54.5", - "stylus-loader": "3.0.1", - "uglifyjs-webpack-plugin": "1.1.8", - "url-loader": "0.6.2", - "webpack": "3.10.0", - "webpack-dev-middleware": "1.12.2", - "webpack-dev-server": "2.11.1", - "webpack-merge": "4.1.1", - "webpack-sources": "1.1.0", - "webpack-subresource-integrity": "1.0.4" - } - }, - "@angular/common": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.5.tgz", - "integrity": "sha512-jagCxo+75pcTwjuO1ZheIiTlKBJ6REFKFWoUPTzaSS6fnzReFJ+VPf4Pb0bWtHL1lWvbvnzmITOJPB9wmuM3fg==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/compiler": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.5.tgz", - "integrity": "sha512-YU/r5omexkrrBF3bZaseWrc2Iotk6hIdUWkPIL3gPC0hKJ3wBeB3sHCBujPQXktWdMBbQRujNSMZtgra3Oh1xQ==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/compiler-cli": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.2.5.tgz", - "integrity": "sha512-jRFMxUKpodzOBKdZc6OMse+CjK6xfTJssZQrYeIyqz2daobaIsMZP2hZX8s/PCfV8Vxa7XFwCJb7Fq2uyZKfHg==", - "dev": true, - "requires": { - "chokidar": "1.7.0", - "minimist": "1.2.0", - "reflect-metadata": "0.1.12", - "tsickle": "0.26.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "@angular/core": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.5.tgz", - "integrity": "sha512-Uo7R3LrsvA24JkRbwXWUZWp7NSEpwdTUxT1NScyjrBK+t8ybSL5/42Jo21md5M4pjeCsIgUXlGoCm1QtT5aYnQ==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/flex-layout": { - "version": "2.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.12.tgz", - "integrity": "sha512-QTOKZxehYTh8fj64V/pNVWNbfNtebSbssyMIXiGJuHTzfyF7GYdRmtjoR2pNpllycz3rE5NYX77EB140Y6BCnw==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/forms": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.5.tgz", - "integrity": "sha512-3feqqTuv9rIu7ZOsLCtM/ugNFz5RPujLHkE8bU1gsMM4/eMYruIFir2vbjnhMkD3K6KptEg4iO6tDW18diwXug==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/http": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-5.2.5.tgz", - "integrity": "sha512-VqTCkAnebe+M9Bqrfp1QYpBQCTbXide/NxrQfwiJY87kjKFeRBuy9/XH/2S5wIwlF5Yx3bmlaIufd9VI5r/0aQ==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/language-service": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-5.2.5.tgz", - "integrity": "sha512-UWNbECu8svXmrgbTL03Fr+Dn06aPCZZLScmCOGVT5lkdsiJPAJpWAvKVM2Y0nzH0PmvekHw7INtV5lwfJOijYQ==", - "dev": true - }, - "@angular/material": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.2.1.tgz", - "integrity": "sha512-94VmxclpIwXAxeudz9AfMg0m46/TEx/GsDZ7R9yOtrbptAr9xSgOumiEqET4Xjb35/mzgD/PKqlcMWyHJCkyVQ==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/platform-browser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.5.tgz", - "integrity": "sha512-iPAuoG/c3pD3hnk1g0VgJu/pzNITvLQyT0W71MDMSuxLxs291kq+U2jklm40pStISd1mPbCNKmvz/7M+WbdLhg==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/platform-browser-dynamic": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.5.tgz", - "integrity": "sha512-IMEe2qUTC3CA3KoswmJJs+O2Lkyd5GXgl5ULupqhhm/TOL2FLk00kwv8k3Epaf2d1wXcjK3BMG7aAwc6RLH7QA==", - "requires": { - "tslib": "1.9.0" - } - }, - "@angular/platform-server": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-5.2.5.tgz", - "integrity": "sha512-IpuEDNyoVfGO94jd1s+4IgoTBkWigwqD4YQTpcsC1mdY2Ax7NXXTAx28ZQF5EvPbSxsHGB5zG3oR7KE7GMNhYQ==", - "requires": { - "domino": "1.0.30", - "tslib": "1.9.0", - "xhr2": "0.1.4" - } - }, - "@angular/router": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-5.2.5.tgz", - "integrity": "sha512-I8U0iy59lz0dAxU4zxRQHagfUPWF+MikLNMirRL1lrA49PG+5K1tiuIQ6p+8fZFAJ5UXwNHyXqYuWqsKRiVBHQ==", - "requires": { - "tslib": "1.9.0" - } - }, - "@ngtools/json-schema": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.1.0.tgz", - "integrity": "sha1-w6DFRNYjkqzCgTpCyKDcb1j4aSI=", - "dev": true - }, - "@ngtools/webpack": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.9.7.tgz", - "integrity": "sha512-D5QuaT9wENeM2j9g2qvW9Ls1tGqRz26Lp+jxwb2ZGFep7Ik1fFOX3ROLfgkxNlxZGVmbxJjsfrYUCyGlzj8gWg==", - "dev": true, - "requires": { - "chalk": "2.2.2", - "enhanced-resolve": "3.4.1", - "loader-utils": "1.1.0", - "magic-string": "0.22.4", - "semver": "5.5.0", - "source-map": "0.5.7", - "tree-kill": "1.2.0", - "webpack-sources": "1.1.0" - } - }, - "@ngx-translate/core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-9.1.1.tgz", - "integrity": "sha1-rhA5KINrip4Gn9Li52+iGYzH5ig=" - }, - "@schematics/angular": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.1.17.tgz", - "integrity": "sha512-PHE5gk/ogPY/aN94dbbtauHMCq+/7w4Kdcl7tGmSS8mPKEI0wa6XJi//Wq/tHi55lb2fP58oEZU6n6w/wQascw==", - "dev": true, - "requires": { - "typescript": "2.6.2" - }, - "dependencies": { - "typescript": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", - "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", - "dev": true - } - } - }, - "@types/d3": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-4.12.0.tgz", - "integrity": "sha512-F5lVj6c2G/WPbKFk4ZVxTS8F/6IRknWcheswQcycMjBh17iJ+bRfNUtn0yvtIHtRPQSanI9Dx2U8rSSA/I+ecQ==", - "requires": { - "@types/d3-array": "1.2.1", - "@types/d3-axis": "1.0.9", - "@types/d3-brush": "1.0.7", - "@types/d3-chord": "1.0.6", - "@types/d3-collection": "1.0.5", - "@types/d3-color": "1.0.5", - "@types/d3-dispatch": "1.0.5", - "@types/d3-drag": "1.2.0", - "@types/d3-dsv": "1.0.31", - "@types/d3-ease": "1.0.7", - "@types/d3-force": "1.1.0", - "@types/d3-format": "1.2.1", - "@types/d3-geo": "1.9.4", - "@types/d3-hierarchy": "1.1.0", - "@types/d3-interpolate": "1.1.6", - "@types/d3-path": "1.0.6", - "@types/d3-polygon": "1.0.5", - "@types/d3-quadtree": "1.0.5", - "@types/d3-queue": "3.0.5", - "@types/d3-random": "1.1.0", - "@types/d3-request": "1.0.2", - "@types/d3-scale": "2.0.0", - "@types/d3-selection": "1.3.0", - "@types/d3-shape": "1.2.2", - "@types/d3-time": "1.0.7", - "@types/d3-time-format": "2.1.0", - "@types/d3-timer": "1.0.6", - "@types/d3-transition": "1.1.1", - "@types/d3-voronoi": "1.1.7", - "@types/d3-zoom": "1.7.0" - } - }, - "@types/d3-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.1.tgz", - "integrity": "sha512-YBaAfimGdWE4nDuoGVKsH89/dkz2hWZ0i8qC+xxqmqi+XJ/aXiRF0jPtzXmN7VdkpVjy1xuDmM5/m1FNuB6VWA==" - }, - "@types/d3-axis": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.9.tgz", - "integrity": "sha512-fNUnI2a0F3xiE/nGrTdDpZG4sdcRIB4X59p9jgY8O7RQiKrVqyb433YCCYSqVID4CVyoq5v3bSFliUEk0FOMsw==", - "requires": { - "@types/d3-selection": "1.3.0" - } - }, - "@types/d3-brush": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.0.7.tgz", - "integrity": "sha1-BcMEQPTVN/0j+Xaw5sS6IjAB70U=", - "requires": { - "@types/d3-selection": "1.3.0" - } - }, - "@types/d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha1-BYnrl6MZH07a8Xt73kmEYokM4ew=" - }, - "@types/d3-collection": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.5.tgz", - "integrity": "sha1-ux86qXzcjYgWRVQbnWz4ft/um8M=" - }, - "@types/d3-color": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.0.5.tgz", - "integrity": "sha1-ytdV8Pxt57cPpuXgivqB70wiSN4=" - }, - "@types/d3-dispatch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.5.tgz", - "integrity": "sha1-8fkYe1OOywUVdWnY3C9w37BPG1I=" - }, - "@types/d3-drag": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.0.tgz", - "integrity": "sha512-AePmm0sXj0Tpl0uQWvwmbAf1QR3yCy9aRhjJ9mRDDSZlHBdY0SCpUtdZC9uG9Q+pyHT/dEt1R2FT/sj+5k/bVA==", - "requires": { - "@types/d3-selection": "1.3.0" - } - }, - "@types/d3-dsv": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.31.tgz", - "integrity": "sha512-UCAVZdwd2NkrbkF1lZu9vzTlmUENRRrPCubyhDPlG8Ye1B8Xr2PNvk/Tp8tMm6sPoWZWagri6/P9H+t7WqkGDg==" - }, - "@types/d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha1-k6MBhovp4VBh89RDQ7GrP4rLbwk=" - }, - "@types/d3-force": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.1.0.tgz", - "integrity": "sha512-a39Uu/ltLaMpj6K0elEB1oAqhx9rlTB5X/O75uTUqyTW2CfjhPXg6hFsX1lF8oeMc29kqGJZ4g9Pf6mET25bVw==" - }, - "@types/d3-format": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.2.1.tgz", - "integrity": "sha512-dXhA9jzCbzu6Va8ZVUQ60nu6jqA5vhPhKGR4nY3lDYfjT05GyKEKuEhfwTaSTybWczY4nLEkzv9wLQCQd5+3cA==" - }, - "@types/d3-geo": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.9.4.tgz", - "integrity": "sha512-DoigJorMGGIG9K4n980zz5g1XnvhDhNy7rk/0O8KCpFPpUZ9hyAgN0ZHXhbtIelxhJhMZxwMRe2soxx/Fhx4Hg==", - "requires": { - "@types/geojson": "7946.0.1" - } - }, - "@types/d3-hierarchy": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.0.tgz", - "integrity": "sha1-UPHuBShAY4A1y91KyrH8NHCQWQc=" - }, - "@types/d3-interpolate": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.1.6.tgz", - "integrity": "sha1-ZAQbFcnAMsNI2hsiuqvFn6TRYTY=", - "requires": { - "@types/d3-color": "1.0.5" - } - }, - "@types/d3-path": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.6.tgz", - "integrity": "sha512-YHW4cs+wOU9gFUzudjJs9TkrB/8GOgKhq32ZyNaZ2rzZjOhkqG486sGr9XSh4C91CcgIg1FRGoDaN29Ropx9nw==" - }, - "@types/d3-polygon": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.5.tgz", - "integrity": "sha1-Na1U7YTDnX6fElK2U1vmAL5srOI=" - }, - "@types/d3-quadtree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.5.tgz", - "integrity": "sha1-HOHmWerkUw3wyxJ/KX8XQaNnqC4=" - }, - "@types/d3-queue": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-queue/-/d3-queue-3.0.5.tgz", - "integrity": "sha1-Pky+Kv9h22oLK4xIACmeTsasyFA=" - }, - "@types/d3-random": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.0.tgz", - "integrity": "sha1-LdCPEVnHBxknDkp8g0r4XIuI0sM=" - }, - "@types/d3-request": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-request/-/d3-request-1.0.2.tgz", - "integrity": "sha1-2524FU9HgWWEcGxub3Ar5m8i9L4=", - "requires": { - "@types/d3-dsv": "1.0.31" - } - }, - "@types/d3-scale": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.0.0.tgz", - "integrity": "sha512-fFLSdP3p9qQQ3W6ouO3GBI4Qg94CSykTWVc61U8SI1V62dfBWtOigBj5voxDcOniwh9MjKzTHldMSsGJ5qAFpA==", - "requires": { - "@types/d3-time": "1.0.7" - } - }, - "@types/d3-selection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.3.0.tgz", - "integrity": "sha512-1SJhi3kTk/SHHIE6XkHuHU2REYkbSOjkQuo3HT71FOTs8/tjeGcvtXMsX4N3kU1UE1nVG+A5pg7TSjuJ4zUN3A==" - }, - "@types/d3-shape": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.2.2.tgz", - "integrity": "sha512-Ydksrces8J5WP/NXhZ/CcDx/XZZ8b7MDX+u6WGQXwEWfmimJn9eYHiD7QR4BLe3zBiAOQmmiGAwRBKUDp5zb1g==", - "requires": { - "@types/d3-path": "1.0.6" - } - }, - "@types/d3-time": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.7.tgz", - "integrity": "sha512-X5ZQYiJIM38XygNwld4gZ++Vtw2ftgo3KOfZOY4n/sCudUxclxf/3THBvuG8UqSV+EQ0ezYjT5eyvcrrmixOWA==" - }, - "@types/d3-time-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", - "integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==" - }, - "@types/d3-timer": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.6.tgz", - "integrity": "sha1-eG1OIHMa3wOvLF32yG/ilmf+Qps=" - }, - "@types/d3-transition": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.1.tgz", - "integrity": "sha512-GHTghl0YYB8gGgbyKxVLHyAp9Na0HqsX2U7M0u0lGw4IdfEaslooykweZ8fDHW13T+KZeZAuzhbmqBZVFO+6kg==", - "requires": { - "@types/d3-selection": "1.3.0" - } - }, - "@types/d3-voronoi": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.7.tgz", - "integrity": "sha512-/dHFLK5jhXTb/W4XEQcFydVk8qlIAo85G3r7+N2fkBFw190l0R1GQ8C1VPeXBb2GfSU5GbT2hjlnE7i7UY5Gvg==" - }, - "@types/d3-zoom": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.0.tgz", - "integrity": "sha512-eIivt2ehMUXqS0guuVzRSMr5RGhO958g9EKxIJv3Z23suPnX4VQI9k1TC/bLuwKq0IWp9a1bEEcIy+PNJv9BtA==", - "requires": { - "@types/d3-interpolate": "1.1.6", - "@types/d3-selection": "1.3.0" - } - }, - "@types/geojson": { - "version": "7946.0.1", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.1.tgz", - "integrity": "sha512-BXY6tH16Snp/ZdX6cFlBD8yfEArcZemzxEGciXkMmp1/tU76oyqkxJq91JQzT8SXWzRPwj//dw0/FdCSnnT8mw==" - }, - "@types/jasmine": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.6.tgz", - "integrity": "sha512-clg9raJTY0EOo5pVZKX3ZlMjlYzVU73L71q5OV1jhE2Uezb7oF94jh4CvwrW6wInquQAdhOxJz5VDF2TLUGmmA==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz", - "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", - "dev": true, - "requires": { - "@types/jasmine": "2.8.6" - } - }, - "@types/node": { - "version": "6.0.101", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.101.tgz", - "integrity": "sha512-IQ7V3D6+kK1DArTqTBrnl3M+YgJZLw8ta8w3Q9xjR79HaJzMAoTbZ8TNzUTztrkCKPTqIstE2exdbs1FzsYLUw==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "2.53.43", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz", - "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==", - "dev": true - }, - "@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", - "dev": true - }, - "@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", - "dev": true - }, - "JSONStream": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", - "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "dev": true, - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", - "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "4.0.13" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "acorn-node": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", - "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", - "dev": true, - "requires": { - "acorn": "5.4.1", - "xtend": "4.0.1" - } - }, - "addressparser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", - "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=", - "dev": true, - "optional": true - }, - "adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=", - "dev": true - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", - "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", - "dev": true, - "requires": { - "extend": "3.0.1", - "semver": "5.0.3" - }, - "dependencies": { - "semver": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", - "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", - "dev": true - } - } - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "ajv-keywords": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz", - "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "amqplib": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz", - "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==", - "dev": true, - "optional": true, - "requires": { - "bitsyntax": "0.0.4", - "bluebird": "3.5.1", - "buffer-more-ints": "0.0.2", - "readable-stream": "1.1.14", - "safe-buffer": "5.1.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true, - "optional": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "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, - "optional": true - } - } - }, - "angular2-toaster": { - "version": "5.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/angular2-toaster/-/angular2-toaster-5.0.0-alpha.1.tgz", - "integrity": "sha512-4fG+JBLExcpVG7V/pt5Lhi3C7nDxnqKTKIJarwe41MP9hKP94hLExlWHsEN6nhG+nLIecp+LBamP3LFOjZCFMQ==" - }, - "angular2-uuid": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/angular2-uuid/-/angular2-uuid-1.1.1.tgz", - "integrity": "sha1-cvA81TK39AAy6x7PufhFc4S+lW4=" - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.1" - }, - "dependencies": { - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - } - } - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - } - }, - "app-root-path": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", - "integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=", - "dev": true - }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true, - "requires": { - "default-require-extensions": "1.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "dev": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-filter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", - "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", - "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", - "dev": true - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.10.0" - } - }, - "array-map": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", - "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", - "dev": true - }, - "array-reduce": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", - "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", - "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", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "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", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true, - "optional": true - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.2.tgz", - "integrity": "sha512-ufWX953VU1eIuWqxS0nRDMYlGyFH+yxln5CsmIHlpzEt3fdYqUnRtsFt0XAsQot8OaVCwFqxT1RiwvtzYjeYeg==", - "dev": true, - "optional": true - }, - "astw": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", - "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", - "dev": true, - "requires": { - "acorn": "4.0.13" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "dev": true, - "requires": { - "lodash": "4.17.5" - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true, - "optional": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", - "dev": true - }, - "autoprefixer": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", - "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", - "dev": true, - "requires": { - "browserslist": "2.11.3", - "caniuse-lite": "1.0.30000808", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "6.0.18", - "postcss-value-parser": "3.3.0" - } - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true - }, - "axios": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", - "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", - "dev": true, - "optional": true, - "requires": { - "follow-redirects": "1.0.0" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.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 - } - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - } - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.3", - "regenerator-runtime": "0.11.1" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.5" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", - "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==", - "dev": true - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true - }, - "bitsyntax": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.0.4.tgz", - "integrity": "sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI=", - "dev": true, - "optional": true, - "requires": { - "buffer-more-ints": "0.0.2" - } - }, - "bl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", - "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "2.0.6" - }, - "dependencies": { - "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, - "optional": 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, - "optional": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "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, - "optional": true - } - } - }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", - "dev": true - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "optional": true, - "requires": { - "inherits": "2.0.3" - } - }, - "blocking-proxy": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.5.tgz", - "integrity": "sha1-RikF4Nz76pcPQao3Ij3anAexkSs=", - "dev": true, - "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.15" - }, - "dependencies": { - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "2.1.1", - "deep-equal": "1.0.1", - "dns-equal": "1.0.0", - "dns-txt": "2.0.2", - "multicast-dns": "6.2.3", - "multicast-dns-service-types": "1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-pack": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", - "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "combine-source-map": "0.8.0", - "defined": "1.0.0", - "safe-buffer": "5.1.1", - "through2": "2.0.3", - "umd": "3.0.1" - } - }, - "browser-resolve": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browserify": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz", - "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "assert": "1.4.1", - "browser-pack": "6.0.4", - "browser-resolve": "1.11.2", - "browserify-zlib": "0.2.0", - "buffer": "5.1.0", - "cached-path-relative": "1.0.1", - "concat-stream": "1.5.2", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "defined": "1.0.0", - "deps-sort": "2.0.0", - "domain-browser": "1.1.7", - "duplexer2": "0.1.4", - "events": "1.1.1", - "glob": "7.1.2", - "has": "1.0.1", - "htmlescape": "1.1.1", - "https-browserify": "1.0.0", - "inherits": "2.0.3", - "insert-module-globals": "7.0.1", - "labeled-stream-splicer": "2.0.0", - "module-deps": "4.1.1", - "os-browserify": "0.3.0", - "parents": "1.0.1", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "read-only-stream": "2.0.0", - "readable-stream": "2.3.4", - "resolve": "1.5.0", - "shasum": "1.0.2", - "shell-quote": "1.6.1", - "stream-browserify": "2.0.1", - "stream-http": "2.8.0", - "string_decoder": "1.0.3", - "subarg": "1.0.0", - "syntax-error": "1.4.0", - "through2": "2.0.3", - "timers-browserify": "1.4.2", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4", - "xtend": "4.0.1" - }, - "dependencies": { - "buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", - "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", - "dev": true, - "requires": { - "base64-js": "1.2.3", - "ieee754": "1.1.8" - } - }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - }, - "dependencies": { - "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.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "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 - } - } - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "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 - }, - "timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", - "dev": true, - "requires": { - "process": "0.11.10" - } - } - } - }, - "browserify-aes": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", - "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", - "dev": true, - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "dev": true, - "requires": { - "browserify-aes": "1.1.1", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.3" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "1.0.6" - } - }, - "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", - "dev": true, - "requires": { - "caniuse-lite": "1.0.30000808", - "electron-to-chromium": "1.3.33" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.2.3", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-more-ints": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", - "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "buildmail": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-4.0.1.tgz", - "integrity": "sha1-h393OLeHKYccmhBeO4N9K+EaenI=", - "dev": true, - "optional": true, - "requires": { - "addressparser": "1.0.1", - "libbase64": "0.1.0", - "libmime": "3.0.0", - "libqp": "1.1.0", - "nodemailer-fetch": "1.6.0", - "nodemailer-shared": "1.1.0", - "punycode": "1.4.1" - } - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.2.tgz", - "integrity": "sha512-dljb7dk1jqO5ogE+dRpoR9tpHYv5xz9vPSNunh1+0wRuNdYxmzp9WmsyokgW/DUF1FDRVA/TMsmxt027R8djbQ==", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.1", - "mississippi": "1.3.1", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.2.2", - "unique-filename": "1.1.0", - "y18n": "3.2.1" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "cached-path-relative": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", - "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", - "dev": true - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "2.3.2", - "upper-case": "1.1.3" - } - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" - } - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000808", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "1.0.30000808", - "electron-to-chromium": "1.3.33" - } - } - } - }, - "caniuse-db": { - "version": "1.0.30000808", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000808.tgz", - "integrity": "sha1-MN/YMAnVcE8C3/s3clBo7RKjZrs=", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30000808", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000808.tgz", - "integrity": "sha512-vT0JLmHdvq1UVbYXioxCXHYdNw55tyvi+IUWyX0Zeh1OFQi2IllYtm38IJnSgHWCv/zUnX1hdhy3vMJvuTNSqw==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - }, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - } - } - }, - "chalk": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", - "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "chart.js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.1.tgz", - "integrity": "sha512-pX1oQAY86MiuyZ2hY593Acbl4MLHKrBBhhmZ1YqSadzQbbsBE2rnd6WISoHjIsdf0WDeC0hbePYCz2ZxkV8L+g==", - "requires": { - "chartjs-color": "2.2.0", - "moment": "2.18.1" - } - }, - "chartjs-color": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", - "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", - "requires": { - "chartjs-color-string": "0.5.0", - "color-convert": "0.5.3" - } - }, - "chartjs-color-string": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", - "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", - "requires": { - "color-name": "1.1.3" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.1.3", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "circular-dependency-plugin": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-4.4.0.tgz", - "integrity": "sha512-yEFtUNUYT4jBykEX5ZOHw+5goA3glGZr9wAXIQqoyakjz5H5TeUmScnWRc52douAhb9eYzK3s7V6bXfNnjFdzg==", - "dev": true - }, - "circular-json": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.1.tgz", - "integrity": "sha512-UjgcRlTAhAkLeXmDe2wK7ktwy/tgAqxiSndTIPiFZuIPLZmzHzWMwUIe9h9m/OokypG7snxCDEuwJshGBdPvaw==", - "dev": true - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "1.1.3" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.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 - } - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "classlist.js": { - "version": "1.1.20150312", - "resolved": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz", - "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k=" - }, - "clean-css": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", - "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - }, - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", - "dev": true - }, - "clone-deep": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz", - "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=", - "dev": true, - "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "3.2.2", - "shallow-clone": "0.1.2" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "1.5.1" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codelyzer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.1.0.tgz", - "integrity": "sha512-a3FCIAS3FNQIACvj7KA4iKvH3c6r7X6t6zXsrtV797QGYPQyCwD1fIEd9yV+ZDamijF3YaZ5fbB7QbUMOJGC/g==", - "dev": true, - "requires": { - "app-root-path": "2.0.1", - "css-selector-tokenizer": "0.7.0", - "cssauron": "1.4.0", - "semver-dsl": "1.0.1", - "source-map": "0.5.7", - "sprintf-js": "1.0.3" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" - } - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "1.0.3", - "color-convert": "1.9.1", - "color-string": "0.3.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - } - } - }, - "color-convert": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", - "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "0.11.4", - "css-color-names": "0.0.4", - "has": "1.0.1" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "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.17.5" - } - }, - "combine-source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", - "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", - "dev": true, - "requires": { - "convert-source-map": "1.1.3", - "inline-source-map": "0.6.2", - "lodash.memoize": "3.0.4", - "source-map": "0.5.7" - }, - "dependencies": { - "convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", - "dev": true - }, - "lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", - "dev": true - } - } - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "commander": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", - "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==" - }, - "common-tags": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz", - "integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compressible": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz", - "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "compression": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz", - "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=", - "dev": true, - "requires": { - "accepts": "1.3.4", - "bytes": "3.0.0", - "compressible": "2.0.12", - "debug": "2.6.9", - "on-headers": "1.0.1", - "safe-buffer": "5.1.1", - "vary": "1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.4", - "typedarray": "0.0.6" - } - }, - "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "1.3.2", - "utils-merge": "1.0.1" - } - }, - "connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.4.1.tgz", - "integrity": "sha512-ojaz8MpS3zoLJT/JbYMusYM+dCEArhW24hGAUPYPydTCS+87NFh2TWr85sywG3So4Q4E68QoerqQ+Ns1g0fhDg==", - "dev": true, - "requires": { - "cacache": "10.0.2", - "find-cache-dir": "1.0.0", - "globby": "7.1.1", - "is-glob": "4.0.0", - "loader-utils": "0.2.17", - "minimatch": "3.0.4", - "p-limit": "1.2.0", - "serialize-javascript": "1.4.0" - }, - "dependencies": { - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - } - } - }, - "core-js": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", - "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" - }, - "core-object": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/core-object/-/core-object-3.1.5.tgz", - "integrity": "sha512-sA2/4+/PZ/KV6CKgjrVrrUVBKCkdDO02CUlQ0YKTQoYUwPYNOtOAcWlbYhd5v/1JqYaA6oZ4sDlOU4ppVw6Wbg==", - "dev": true, - "requires": { - "chalk": "2.2.2" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "0.3.1", - "js-yaml": "3.7.0", - "minimist": "1.2.0", - "object-assign": "4.1.1", - "os-homedir": "1.0.2", - "parse-json": "2.2.0", - "require-from-string": "1.2.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.10" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.10" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "4.1.1", - "which": "1.3.0" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", - "randombytes": "2.0.6", - "randomfill": "1.0.3" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-loader": { - "version": "0.28.9", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.9.tgz", - "integrity": "sha512-r3dgelMm/mkPz5Y7m9SeiGE46i2VsEU/OYbez+1llfxtv8b2y5/b5StaeEvPK3S5tlNQI+tDW/xDIhKJoZgDtw==", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "css-selector-tokenizer": "0.7.0", - "cssnano": "3.10.0", - "icss-utils": "2.1.0", - "loader-utils": "1.1.0", - "lodash.camelcase": "4.3.0", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-modules-extract-imports": "1.2.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0", - "postcss-value-parser": "3.3.0", - "source-list-map": "2.0.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "css-parse": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", - "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", - "dev": true - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", - "domutils": "1.5.1", - "nth-check": "1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "2.3.8" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.1", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000808", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "1.0.30000808", - "electron-to-chromium": "1.3.33" - } - }, - "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.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" - } - }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "1.0.2" - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.37" - } - }, - "d3": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz", - "integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==", - "requires": { - "d3-array": "1.2.1", - "d3-axis": "1.0.8", - "d3-brush": "1.0.4", - "d3-chord": "1.0.4", - "d3-collection": "1.0.4", - "d3-color": "1.0.3", - "d3-dispatch": "1.0.3", - "d3-drag": "1.2.1", - "d3-dsv": "1.0.8", - "d3-ease": "1.0.3", - "d3-force": "1.1.0", - "d3-format": "1.2.2", - "d3-geo": "1.9.1", - "d3-hierarchy": "1.1.5", - "d3-interpolate": "1.1.6", - "d3-path": "1.0.5", - "d3-polygon": "1.0.3", - "d3-quadtree": "1.0.3", - "d3-queue": "3.0.7", - "d3-random": "1.1.0", - "d3-request": "1.0.6", - "d3-scale": "1.0.7", - "d3-selection": "1.3.0", - "d3-shape": "1.2.0", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1", - "d3-timer": "1.0.7", - "d3-transition": "1.1.1", - "d3-voronoi": "1.1.2", - "d3-zoom": "1.7.1" - }, - "dependencies": { - "d3-scale": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", - "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", - "requires": { - "d3-array": "1.2.1", - "d3-collection": "1.0.4", - "d3-color": "1.0.3", - "d3-format": "1.2.2", - "d3-interpolate": "1.1.6", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1" - } - } - } - }, - "d3-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==" - }, - "d3-axis": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz", - "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=" - }, - "d3-brush": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", - "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", - "requires": { - "d3-dispatch": "1.0.3", - "d3-drag": "1.2.1", - "d3-interpolate": "1.1.6", - "d3-selection": "1.3.0", - "d3-transition": "1.1.1" - } - }, - "d3-chord": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", - "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", - "requires": { - "d3-array": "1.2.1", - "d3-path": "1.0.5" - } - }, - "d3-collection": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=" - }, - "d3-color": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=" - }, - "d3-dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" - }, - "d3-drag": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", - "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", - "requires": { - "d3-dispatch": "1.0.3", - "d3-selection": "1.3.0" - } - }, - "d3-dsv": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", - "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", - "requires": { - "commander": "2.14.1", - "iconv-lite": "0.4.19", - "rw": "1.3.3" - } - }, - "d3-ease": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" - }, - "d3-force": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", - "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==", - "requires": { - "d3-collection": "1.0.4", - "d3-dispatch": "1.0.3", - "d3-quadtree": "1.0.3", - "d3-timer": "1.0.7" - } - }, - "d3-format": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz", - "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw==" - }, - "d3-geo": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz", - "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==", - "requires": { - "d3-array": "1.2.1" - } - }, - "d3-hierarchy": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", - "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=" - }, - "d3-interpolate": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz", - "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==", - "requires": { - "d3-color": "1.0.3" - } - }, - "d3-path": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", - "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=" - }, - "d3-polygon": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz", - "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI=" - }, - "d3-quadtree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", - "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=" - }, - "d3-queue": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz", - "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg=" - }, - "d3-random": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz", - "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM=" - }, - "d3-request": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz", - "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==", - "requires": { - "d3-collection": "1.0.4", - "d3-dispatch": "1.0.3", - "d3-dsv": "1.0.8", - "xmlhttprequest": "1.8.0" - } - }, - "d3-scale": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.0.0.tgz", - "integrity": "sha512-Sa2Ny6CoJT7x6dozxPnvUQT61epGWsgppFvnNl8eJEzfJBG0iDBBTJAtz2JKem7Mb+NevnaZiDiIDHsuWkv6vg==", - "requires": { - "d3-array": "1.2.1", - "d3-collection": "1.0.4", - "d3-format": "1.2.2", - "d3-interpolate": "1.1.6", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1" - } - }, - "d3-selection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz", - "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA==" - }, - "d3-shape": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", - "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", - "requires": { - "d3-path": "1.0.5" - } - }, - "d3-time": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz", - "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==" - }, - "d3-time-format": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", - "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", - "requires": { - "d3-time": "1.0.8" - } - }, - "d3-timer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", - "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==" - }, - "d3-transition": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", - "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", - "requires": { - "d3-color": "1.0.3", - "d3-dispatch": "1.0.3", - "d3-ease": "1.0.3", - "d3-interpolate": "1.1.6", - "d3-selection": "1.3.0", - "d3-timer": "1.0.7" - } - }, - "d3-voronoi": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", - "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=" - }, - "d3-zoom": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz", - "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==", - "requires": { - "d3-dispatch": "1.0.3", - "d3-drag": "1.2.1", - "d3-interpolate": "1.1.6", - "d3-selection": "1.3.0", - "d3-transition": "1.1.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", - "dev": true, - "optional": true - }, - "date-fns": { - "version": "2.0.0-alpha.7", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.0-alpha.7.tgz", - "integrity": "sha1-JFrRb5V2Tqur+ywKQf1dAzwg5Xo=" - }, - "date-format": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", - "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true, - "optional": true - }, - "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", - "dev": true, - "requires": { - "strip-bom": "2.0.0" - } - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true, - "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" - } - }, - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dev": true, - "optional": true, - "requires": { - "ast-types": "0.10.2", - "escodegen": "1.9.0", - "esprima": "3.1.3" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true, - "optional": true - } - } - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "6.1.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "p-map": "1.2.0", - "pify": "3.0.0", - "rimraf": "2.6.2" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "denodeify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", - "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "deps-sort": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", - "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "shasum": "1.0.2", - "subarg": "1.0.0", - "through2": "2.0.3" - } - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "detect-node": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", - "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", - "dev": true - }, - "detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", - "dev": true, - "requires": { - "acorn": "5.4.1", - "defined": "1.0.0" - } - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" - } - }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "path-type": "3.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "1.1.5", - "safe-buffer": "5.1.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "1.1.1" - } - }, - "dom-converter": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", - "dev": true, - "requires": { - "utila": "0.3.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "1.0.1", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "domino": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz", - "integrity": "sha512-ikq8WiDSkICdkElud317F2Sigc6A3EDpWsxWBwIZqOl95km4p/Vc9Rj98id7qKgsjDmExj0AVM7JOd4bb647Xg==" - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" - } - }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", - "dev": true, - "optional": true - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "2.3.4" - } - }, - "duplexify": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz", - "integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.4", - "stream-shift": "1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", - "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.33", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz", - "integrity": "sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU=", - "dev": true - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "ember-cli-string-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ember-cli-string-utils/-/ember-cli-string-utils-1.1.0.tgz", - "integrity": "sha1-ObZ3/CgF9VFzc1N2/O8njqpEUqE=", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "engine.io": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.4.tgz", - "integrity": "sha1-PQIRtwpVLOhB/8fahiezAamkFi4=", - "dev": true, - "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.6.9", - "engine.io-parser": "2.1.2", - "uws": "0.14.5", - "ws": "3.3.3" - }, - "dependencies": { - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true, - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - } - } - }, - "engine.io-client": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", - "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.6.9", - "engine.io-parser": "2.1.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "3.3.3", - "xmlhttprequest-ssl": "1.5.5", - "yeast": "0.1.2" - } - }, - "engine.io-parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz", - "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary2": "1.0.2" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "object-assign": "4.1.1", - "tapable": "0.2.8" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "1.0.1" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es-abstract": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", - "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", - "dev": true, - "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.1", - "is-callable": "1.1.3", - "is-regex": "1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" - } - }, - "es5-ext": { - "version": "0.10.37", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", - "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=", - "dev": true, - "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.37", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.37", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.37", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.37" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.37", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz", - "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==", - "dev": true, - "optional": true, - "requires": { - "esprima": "3.1.3", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.5.7" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true, - "optional": true - } - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, - "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.37" - } - }, - "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", - "dev": true, - "requires": { - "original": "1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - } - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "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.5" - }, - "dependencies": { - "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.1" - } - }, - "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": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "exports-loader": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.6.4.tgz", - "integrity": "sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY=", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "source-map": "0.5.7" - } - }, - "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", - "dev": true, - "requires": { - "accepts": "1.3.4", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", - "utils-merge": "1.0.1", - "vary": "1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "extract-text-webpack-plugin": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", - "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", - "dev": true, - "requires": { - "async": "2.6.0", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0", - "webpack-sources": "1.1.0" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true, - "optional": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - }, - "file-loader": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.6.tgz", - "integrity": "sha512-873ztuL+/hfvXbLDJ262PGO6XjERnybJu2gW1/5j8HUfxSiFJI9Hj/DhZ50ZGRUxBvuNiazb/cM2rh9pqrxP6Q==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" - } - }, - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "make-dir": "1.1.0", - "pkg-dir": "2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, - "flush-write-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz", - "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.4" - } - }, - "follow-redirects": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", - "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.9" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.17" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.4" - } - }, - "fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "1.0.0" - } - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.1" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.4" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "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, - "requires": { - "wrappy": "1.0.2" - } - }, - "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.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "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.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.3.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 - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "1.1.14", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true, - "optional": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "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, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "gaze": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", - "dev": true, - "optional": true, - "requires": { - "globule": "1.2.0" - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true, - "optional": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "optional": true, - "requires": { - "is-property": "1.0.2" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.1.tgz", - "integrity": "sha512-7aelVrYqCLuVjq2kEKRTH8fXPTC0xKTkM+G7UlFkEwCXY3sFbSxvY375JoFowOAYbkaU47SrBvOefUlLZZ+6QA==", - "dev": true, - "optional": true, - "requires": { - "data-uri-to-buffer": "1.2.0", - "debug": "2.6.9", - "extend": "3.0.1", - "file-uri-to-path": "1.0.0", - "ftp": "0.3.10", - "readable-stream": "2.3.4" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "dir-glob": "2.0.0", - "glob": "7.1.2", - "ignore": "3.3.7", - "pify": "3.0.0", - "slash": "1.0.0" - } - }, - "globule": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", - "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", - "dev": true, - "optional": true, - "requires": { - "glob": "7.1.2", - "lodash": "4.17.5", - "minimatch": "3.0.4" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" - }, - "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", - "dev": true - }, - "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", - "dev": true, - "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "optional": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "optional": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - } - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "optional": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - } - } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - } - } - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-binary2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", - "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", - "dev": true, - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hipchat-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz", - "integrity": "sha1-ttJJdVQ3wZEII2d5nTupoPI7Ix4=", - "dev": true, - "optional": true, - "requires": { - "lodash": "4.17.5", - "request": "2.81.0" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "dev": true, - "requires": { - "parse-passwd": "1.0.0" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "obuf": "1.1.1", - "readable-stream": "2.3.4", - "wbuf": "1.7.2" - } - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.9", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.9.tgz", - "integrity": "sha512-EZqO91XJwkj8BeLx9C12sKB/AHoTANaZax39vEOP9f/X/9jgJ3r1O2+neabuHqpz5kJO71TapP9JrtCY39su1A==", - "dev": true, - "requires": { - "camel-case": "3.0.0", - "clean-css": "4.1.9", - "commander": "2.14.1", - "he": "1.1.1", - "ncname": "1.0.0", - "param-case": "2.1.1", - "relateurl": "0.2.7", - "uglify-js": "3.3.11" - } - }, - "html-webpack-plugin": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz", - "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "html-minifier": "3.5.9", - "loader-utils": "0.2.17", - "lodash": "4.17.5", - "pretty-error": "2.1.1", - "toposort": "1.0.6" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - } - } - }, - "htmlescape": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", - "dev": true - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.1.0", - "domutils": "1.1.6", - "readable-stream": "1.0.34" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "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 - } - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.3.1" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - } - } - }, - "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", - "dev": true - }, - "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", - "dev": true, - "requires": { - "eventemitter3": "1.2.0", - "requires-port": "1.0.0" - } - }, - "http-proxy-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz", - "integrity": "sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo=", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.9", - "extend": "3.0.1" - } - }, - "http-proxy-middleware": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", - "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", - "dev": true, - "requires": { - "http-proxy": "1.16.2", - "is-glob": "3.1.0", - "lodash": "4.17.5", - "micromatch": "2.3.11" - }, - "dependencies": { - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - } - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "httpntlm": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", - "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", - "dev": true, - "requires": { - "httpreq": "0.4.24", - "underscore": "1.7.0" - } - }, - "httpreq": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", - "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=", - "dev": true - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", - "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.9", - "extend": "3.0.1" - } - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true, - "requires": { - "postcss": "6.0.18" - } - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", - "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", - "dev": true - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true - }, - "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", - "dev": true, - "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", - "dev": true, - "optional": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflection": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.10.0.tgz", - "integrity": "sha1-W//LEZetPoEFD44X4hZoCH7p6y8=", - "dev": true, - "optional": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inline-source-map": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", - "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "insert-module-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", - "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "combine-source-map": "0.7.2", - "concat-stream": "1.5.2", - "is-buffer": "1.1.6", - "lexical-scope": "1.2.0", - "process": "0.11.10", - "through2": "2.0.3", - "xtend": "4.0.1" - }, - "dependencies": { - "combine-source-map": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", - "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", - "dev": true, - "requires": { - "convert-source-map": "1.1.3", - "inline-source-map": "0.6.2", - "lodash.memoize": "3.0.4", - "source-map": "0.5.7" - } - }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - } - }, - "convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", - "dev": true - }, - "lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", - "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.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "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 - } - } - }, - "internal-ip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", - "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", - "dev": true, - "requires": { - "meow": "3.7.0" - } - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "intl": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz", - "integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94=" - }, - "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.11.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", - "dev": true - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", - "dev": true, - "optional": true - }, - "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", - "dev": true, - "optional": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "is-my-ip-valid": "1.0.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-odd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", - "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", - "dev": true, - "requires": { - "is-number": "3.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - } - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "dev": true, - "requires": { - "is-path-inside": "1.0.1" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true, - "optional": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "1.0.1" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "1.1.1" - } - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.2.2.tgz", - "integrity": "sha512-kH5YRdqdbs5hiH4/Rr1Q0cSAGgjh3jTtg8vu9NLebBAoK3adVO4jk81J+TYOkTr2+Q4NLeb1ACvmEt65iG/Vbw==", - "dev": true, - "requires": { - "async": "2.6.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.1.2", - "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "1.9.2", - "istanbul-lib-report": "1.1.3", - "istanbul-lib-source-maps": "1.2.3", - "istanbul-reports": "1.1.4", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "once": "1.4.0" - } - }, - "istanbul-instrumenter-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz", - "integrity": "sha512-alLSEFX06ApU75sm5oWcaVNaiss/bgMRiWTct3g0P0ZZTKjR+6QiCcuVOKDI1kWJgwHEnIXsv/dWm783kPpmtw==", - "dev": true, - "requires": { - "convert-source-map": "1.5.1", - "istanbul-lib-instrument": "1.9.2", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" - } - }, - "istanbul-lib-coverage": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.2.tgz", - "integrity": "sha512-tZYA0v5A7qBSsOzcebJJ/z3lk3oSzH62puG78DbBA1+zupipX2CakDyiPV3pOb8He+jBwVimuwB0dTnh38hX0w==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", - "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", - "dev": true, - "requires": { - "append-transform": "0.4.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.2.tgz", - "integrity": "sha512-nz8t4HQ2206a/3AXi+NHFWEa844DMpPsgbcUteJbt1j8LX1xg56H9rOMnhvcvVvPbW60qAIyrSk44H8ZDqaSSA==", - "dev": true, - "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.1.2", - "semver": "5.5.0" - } - }, - "istanbul-lib-report": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz", - "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "1.1.2", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", - "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", - "dev": true, - "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.1.2", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "istanbul-reports": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.4.tgz", - "integrity": "sha512-DfSTVOTkuO+kRmbO8Gk650Wqm1WRGr6lrdi2EwDK1vxpS71vdlLd613EpzOKdIFioB5f/scJTjeWBnvd1FWejg==", - "dev": true, - "requires": { - "handlebars": "4.0.11" - } - }, - "jasmine": { - "version": "2.99.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.99.0.tgz", - "integrity": "sha1-jKctEC5jm4Z8ZImFbg4YqceqQrc=", - "dev": true, - "requires": { - "exit": "0.1.2", - "glob": "7.1.2", - "jasmine-core": "2.99.1" - }, - "dependencies": { - "jasmine-core": { - "version": "2.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", - "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", - "dev": true - } - } - }, - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - }, - "jasmine-spec-reporter": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", - "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", - "dev": true, - "requires": { - "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.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "1.0.10", - "esprima": "2.7.3" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "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.11" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "karma": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.0.tgz", - "integrity": "sha512-K9Kjp8CldLyL9ANSUctDyxC7zH3hpqXj/K09qVf06K3T/kXaHtFZ5tQciK7OzQu68FLvI89Na510kqQ2LCbpIw==", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "body-parser": "1.18.2", - "browserify": "14.5.0", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.6", - "core-js": "2.5.3", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.16.2", - "isbinaryfile": "3.0.2", - "lodash": "4.17.5", - "log4js": "2.5.3", - "mime": "1.6.0", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.1.5", - "range-parser": "1.2.0", - "rimraf": "2.6.2", - "safe-buffer": "5.1.1", - "socket.io": "2.0.4", - "source-map": "0.6.1", - "tmp": "0.0.33", - "useragent": "2.3.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", - "dev": true, - "requires": { - "fs-access": "1.0.1", - "which": "1.3.0" - } - }, - "karma-coverage-istanbul-reporter": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.1.tgz", - "integrity": "sha512-5og0toMjgLvsL9+TzGH4Rk1D0nr7pMIRJBg29xP4mHMKy/1KUJ12UzoqI6mBNCRFa4nDvZS2MRrN7p+RkZNWxQ==", - "dev": true, - "requires": { - "istanbul-api": "1.2.2", - "minimatch": "3.0.4" - } - }, - "karma-jasmine": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz", - "integrity": "sha1-b+hA51oRYAydkehLM8RY4cRqNSk=", - "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.1.1" - } - }, - "karma-source-map-support": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.2.0.tgz", - "integrity": "sha1-G/gee7SwiWJ6s1LsQXnhF8QGpUA=", - "dev": true, - "requires": { - "source-map-support": "0.4.18" - } - }, - "killable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", - "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "labeled-stream-splicer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", - "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "isarray": "0.0.1", - "stream-splicer": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "less": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", - "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", - "dev": true, - "requires": { - "errno": "0.1.7", - "graceful-fs": "4.1.11", - "image-size": "0.5.5", - "mime": "1.6.0", - "mkdirp": "0.5.1", - "promise": "7.3.1", - "request": "2.81.0", - "source-map": "0.5.7" - } - }, - "less-loader": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.0.5.tgz", - "integrity": "sha1-rhVadAbKxqzSk9eFWH/P8PR4xN0=", - "dev": true, - "requires": { - "clone": "2.1.1", - "loader-utils": "1.1.0", - "pify": "2.3.0" - }, - "dependencies": { - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "optional": true, - "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" - } - }, - "lexical-scope": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", - "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", - "dev": true, - "requires": { - "astw": "2.2.0" - } - }, - "libbase64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", - "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=", - "dev": true - }, - "libmime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz", - "integrity": "sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY=", - "dev": true, - "requires": { - "iconv-lite": "0.4.15", - "libbase64": "0.1.0", - "libqp": "1.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", - "dev": true - } - } - }, - "libqp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", - "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=", - "dev": true - }, - "license-webpack-plugin": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.1.1.tgz", - "integrity": "sha512-TjKOyiC0exqd4Idy/4M8/DETR22dXBZks387DuS5LbslxHiMRXGx/Q2F/j9IUtvEoH5uFvt72vRgk/G6f8j3Dg==", - "dev": true, - "requires": { - "ejs": "2.5.7" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true, - "optional": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.endswith": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.endswith/-/lodash.endswith-4.2.1.tgz", - "integrity": "sha1-/tWawXOO0+I27dcGTsRWRIs3vAk=", - "dev": true - }, - "lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "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==", - "dev": true, - "optional": true - }, - "lodash.startswith": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz", - "integrity": "sha1-xZjErc4YiiflMUVzHNxsDnF3YAw=", - "dev": true - }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log4js": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.5.3.tgz", - "integrity": "sha512-YL/qpTxYtK0iWWbuKCrevDZz5lh+OjyHHD+mICqpjnYGKdNRBvPeh/1uYjkKUemT1CSO4wwLOwphWMpKAnD9kw==", - "dev": true, - "requires": { - "amqplib": "0.5.2", - "axios": "0.15.3", - "circular-json": "0.5.1", - "date-format": "1.2.0", - "debug": "3.1.0", - "hipchat-notifier": "1.1.0", - "loggly": "1.1.1", - "mailgun-js": "0.7.15", - "nodemailer": "2.7.2", - "redis": "2.8.0", - "semver": "5.5.0", - "slack-node": "0.2.0", - "streamroller": "0.7.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "loggly": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/loggly/-/loggly-1.1.1.tgz", - "integrity": "sha1-Cg/B0/o6XsRP3HuJe+uipGlc6+4=", - "dev": true, - "optional": true, - "requires": { - "json-stringify-safe": "5.0.1", - "request": "2.75.0", - "timespan": "2.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true, - "optional": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "form-data": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz", - "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.17" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "optional": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.14.1", - "is-my-json-valid": "2.17.2", - "pinkie-promise": "2.0.1" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", - "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.75.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", - "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "bl": "1.1.2", - "caseless": "0.11.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.0.0", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "6.2.3", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.4.3" - } - }, - "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, - "optional": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true - } - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", - "dev": true - }, - "magic-string": { - "version": "0.22.4", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", - "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", - "dev": true, - "requires": { - "vlq": "0.2.3" - } - }, - "mailcomposer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", - "integrity": "sha1-DhxEsqB890DuF9wUm6AJ8Zyt/rQ=", - "dev": true, - "optional": true, - "requires": { - "buildmail": "4.0.1", - "libmime": "3.0.0" - } - }, - "mailgun-js": { - "version": "0.7.15", - "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.7.15.tgz", - "integrity": "sha1-7jZqINrGTDwVwD1sGz4O15UlKrs=", - "dev": true, - "optional": true, - "requires": { - "async": "2.1.5", - "debug": "2.2.0", - "form-data": "2.1.4", - "inflection": "1.10.0", - "is-stream": "1.1.0", - "path-proxy": "1.0.0", - "proxy-agent": "2.0.0", - "q": "1.4.1", - "tsscmp": "1.0.5" - }, - "dependencies": { - "async": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.5.tgz", - "integrity": "sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw=", - "dev": true, - "optional": true, - "requires": { - "lodash": "4.17.5" - } - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "optional": true, - "requires": { - "ms": "0.7.1" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true, - "optional": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true, - "optional": true - } - } - }, - "make-dir": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", - "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "make-error": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", - "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", - "dev": true - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "1.0.1" - } - }, - "material-design-icons-iconfont": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-3.0.3.tgz", - "integrity": "sha1-FUoQhAR9Ticjf6f1o34Qdc7qbfI=" - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "1.2.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "0.1.7", - "readable-stream": "2.3.4" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mississippi": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.1.tgz", - "integrity": "sha512-/6rB8YXFbAtsUVRphIRQqB0+9c7VaPHCjVtvto+JqwVxgz8Zz+I+f68/JgQ+Pb4VlZb2svA9OtdXnHHsZz7ltg==", - "dev": true, - "requires": { - "concat-stream": "1.6.0", - "duplexify": "3.5.3", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.0.2", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "1.0.3", - "pumpify": "1.4.0", - "stream-each": "1.2.2", - "through2": "2.0.3" - } - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "module-deps": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", - "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "browser-resolve": "1.11.2", - "cached-path-relative": "1.0.1", - "concat-stream": "1.5.2", - "defined": "1.0.0", - "detective": "4.7.1", - "duplexer2": "0.1.4", - "inherits": "2.0.3", - "parents": "1.0.1", - "readable-stream": "2.3.4", - "resolve": "1.5.0", - "stream-combiner2": "1.1.1", - "subarg": "1.0.0", - "through2": "2.0.3", - "xtend": "4.0.1" - }, - "dependencies": { - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - }, - "dependencies": { - "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.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - } - } - }, - "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 - }, - "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 - } - } - }, - "moment": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "1.3.1", - "thunky": "1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mydaterangepicker": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/mydaterangepicker/-/mydaterangepicker-4.2.1.tgz", - "integrity": "sha1-8GPkdHAWJZua2IIVnvwcffCSIc0=" - }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.7.tgz", - "integrity": "sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "is-odd": "1.0.0", - "kind-of": "5.1.0", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "ncname": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", - "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", - "dev": true, - "requires": { - "xml-char-classes": "1.0.0" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "dev": true, - "optional": true - }, - "ng2-charts": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-1.6.0.tgz", - "integrity": "sha512-9w0WH69x5/nuqC1og2WaY39NbaBqTGIP1+5gZaH7/KPN6UEPonNg/pYnsIVklLj1DWPWXKa8+XXIJZ1jy5nLxg==", - "requires": { - "chart.js": "2.7.1" - } - }, - "ng2-cookies": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ng2-cookies/-/ng2-cookies-1.0.12.tgz", - "integrity": "sha1-Pz5hPgE3sGSbcFxngHS0vQgUnMw=" - }, - "ngx-loading": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/ngx-loading/-/ngx-loading-1.0.14.tgz", - "integrity": "sha1-GXWMM+o/qbuW3KH0DKGdTYa4BCw=" - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "1.1.4" - } - }, - "node-forge": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", - "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", - "dev": true - }, - "node-gyp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", - "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.5", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.0" - }, - "dependencies": { - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.1" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - } - } - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", - "dev": true, - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.4", - "stream-browserify": "2.0.1", - "stream-http": "2.8.0", - "string_decoder": "1.0.3", - "timers-browserify": "2.0.6", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4" - } - }, - "node-modules-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/node-modules-path/-/node-modules-path-1.0.1.tgz", - "integrity": "sha1-QAlrCM560OoUaAhjr0ScfHWl0cg=", - "dev": true - }, - "node-sass": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.7.2.tgz", - "integrity": "sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA==", - "dev": true, - "optional": true, - "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.2", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.1", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.8.0", - "node-gyp": "3.6.2", - "npmlog": "4.1.2", - "request": "2.79.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0", - "true-case-path": "1.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true, - "optional": 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.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "optional": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.14.1", - "is-my-json-valid": "2.17.2", - "pinkie-promise": "2.0.1" - } - }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.79.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", - "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.4.3", - "uuid": "3.2.1" - } - }, - "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 - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true - } - } - }, - "nodemailer": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", - "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=", - "dev": true, - "optional": true, - "requires": { - "libmime": "3.0.0", - "mailcomposer": "4.0.1", - "nodemailer-direct-transport": "3.3.2", - "nodemailer-shared": "1.1.0", - "nodemailer-smtp-pool": "2.8.2", - "nodemailer-smtp-transport": "2.7.2", - "socks": "1.1.9" - }, - "dependencies": { - "socks": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", - "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=", - "dev": true, - "optional": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" - } - } - } - }, - "nodemailer-direct-transport": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz", - "integrity": "sha1-6W+vuQNYVglH5WkBfZfmBzilCoY=", - "dev": true, - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-fetch": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", - "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=", - "dev": true - }, - "nodemailer-shared": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", - "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", - "dev": true, - "requires": { - "nodemailer-fetch": "1.6.0" - } - }, - "nodemailer-smtp-pool": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz", - "integrity": "sha1-LrlNbPhXgLG0clzoU7nL1ejajHI=", - "dev": true, - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "nodemailer-wellknown": "0.1.10", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-smtp-transport": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz", - "integrity": "sha1-A9ccdjFPFKx9vHvwM6am0W1n+3c=", - "dev": true, - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "nodemailer-wellknown": "0.1.10", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-wellknown": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz", - "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=", - "dev": true - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "2.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, - "requires": { - "boolbase": "1.0.0" - } - }, - "null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - } - } - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "obuf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", - "integrity": "sha1-EEEktsYCxnlogaBCVB0220OlJk4=", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "opn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", - "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==", - "dev": true, - "requires": { - "is-wsl": "1.1.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.2" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "optional": true, - "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true, - "optional": true - } - } - }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, - "original": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", - "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", - "dev": true, - "requires": { - "url-parse": "1.0.5" - }, - "dependencies": { - "url-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", - "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", - "dev": true, - "requires": { - "querystringify": "0.0.4", - "requires-port": "1.0.0" - } - } - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.2.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pac-proxy-agent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz", - "integrity": "sha512-QBELCWyLYPgE2Gj+4wUEiMscHrQ8nRPBzYItQNOHWavwBt25ohZHQC4qnd5IszdVVrFbLsQ+dPkm6eqdjJAmwQ==", - "dev": true, - "optional": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.9", - "extend": "3.0.1", - "get-uri": "2.0.1", - "http-proxy-agent": "1.0.0", - "https-proxy-agent": "1.0.0", - "pac-resolver": "2.0.0", - "raw-body": "2.3.2", - "socks-proxy-agent": "2.1.1" - } - }, - "pac-resolver": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-2.0.0.tgz", - "integrity": "sha1-mbiNLxk/ve78HJpSnB8yYKtSd80=", - "dev": true, - "optional": true, - "requires": { - "co": "3.0.6", - "degenerator": "1.0.4", - "ip": "1.0.1", - "netmask": "1.0.6", - "thunkify": "2.1.2" - }, - "dependencies": { - "co": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/co/-/co-3.0.6.tgz", - "integrity": "sha1-FEXyJsXrlWE45oyawwFn6n0ua9o=", - "dev": true, - "optional": true - }, - "ip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.0.1.tgz", - "integrity": "sha1-x+NWzeoiWucbNtcPLnGpK6TkJZA=", - "dev": true, - "optional": true - } - } - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.4" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "2.3.2" - } - }, - "parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", - "dev": true, - "requires": { - "path-platform": "0.11.15" - } - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "dev": true, - "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.1.1", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.14" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", - "dev": true - }, - "path-proxy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-proxy/-/path-proxy-1.0.0.tgz", - "integrity": "sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4=", - "dev": true, - "optional": true, - "requires": { - "inflection": "1.3.8" - }, - "dependencies": { - "inflection": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.3.8.tgz", - "integrity": "sha1-y9Fg2p91sUw8xjV41POWeEvzAU4=", - "dev": true, - "optional": true - } - } - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", - "dev": true, - "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.10" - } - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "2.1.0" - } - }, - "portfinder": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", - "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", - "dev": true, - "requires": { - "async": "1.5.2", - "debug": "2.6.9", - "mkdirp": "0.5.1" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "6.0.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.18.tgz", - "integrity": "sha512-X8MyLi3OYI1o71u0SsefWLpGBo5xnGiK1Pn+nrZFplc671Ts7L8aPwEbPIO8AWpulK5wuaVzyM9Rw6R8o7hYBw==", - "dev": true, - "requires": { - "chalk": "2.3.1", - "source-map": "0.6.1", - "supports-color": "5.2.0" - }, - "dependencies": { - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "1.1.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "uniqs": "2.0.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-import": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", - "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", - "dev": true, - "requires": { - "postcss": "6.0.18", - "postcss-value-parser": "3.3.0", - "read-cache": "1.0.0", - "resolve": "1.5.0" - } - }, - "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", - "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "dev": true, - "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1", - "postcss-load-options": "1.2.0", - "postcss-load-plugins": "2.3.0" - } - }, - "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", - "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "dev": true, - "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" - } - }, - "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", - "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", - "dev": true, - "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" - } - }, - "postcss-loader": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.0.tgz", - "integrity": "sha512-S/dKzpDwGFmP9g8eyCu9sUIV+/+3UooeTpYlsKf23qKDdrhHuA4pTSfytVu0rEJ0iDqUavXrgtOPq5KhNyNMOw==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "postcss": "6.0.18", - "postcss-load-config": "1.2.0", - "schema-utils": "0.4.5" - }, - "dependencies": { - "ajv": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", - "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", - "dev": true, - "requires": { - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "6.1.1", - "ajv-keywords": "3.1.0" - } - } - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "1.0.30000808", - "electron-to-chromium": "1.3.33" - } - }, - "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.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", - "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", - "dev": true, - "requires": { - "postcss": "6.0.18" - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.18" - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.18" - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.18" - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "5.2.18" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, - "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "uniqs": "2.0.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-url": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.0.tgz", - "integrity": "sha512-VBP6uf6iL3AZra23nkPkOEkS/5azj1xf/toRrjfkolfFEgg9Gyzg9UhJZeIsz12EGKZTNVeGbPa2XtaZm/iZvg==", - "dev": true, - "requires": { - "mime": "1.6.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "postcss": "6.0.18", - "xxhashjs": "0.2.2" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "uniqs": "2.0.0" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "2.0.1", - "utila": "0.4.0" - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "requires": { - "asap": "2.0.6" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "protractor": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz", - "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=", - "dev": true, - "requires": { - "@types/node": "6.0.101", - "@types/q": "0.0.32", - "@types/selenium-webdriver": "2.53.43", - "blocking-proxy": "0.0.5", - "chalk": "1.1.3", - "glob": "7.1.2", - "jasmine": "2.99.0", - "jasminewd2": "2.2.0", - "optimist": "0.6.1", - "q": "1.4.1", - "saucelabs": "1.3.0", - "selenium-webdriver": "3.0.1", - "source-map-support": "0.4.18", - "webdriver-js-extender": "1.0.0", - "webdriver-manager": "12.0.6" - }, - "dependencies": { - "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=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "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 - }, - "webdriver-manager": { - "version": "12.0.6", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz", - "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", - "dev": true, - "requires": { - "adm-zip": "0.4.7", - "chalk": "1.1.3", - "del": "2.2.2", - "glob": "7.1.2", - "ini": "1.3.5", - "minimist": "1.2.0", - "q": "1.4.1", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.5.0", - "xml2js": "0.4.19" - } - } - } - }, - "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", - "dev": true, - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.5.2" - } - }, - "proxy-agent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-2.0.0.tgz", - "integrity": "sha1-V+tTR6qAXXTsaByyVknbo5yTNJk=", - "dev": true, - "optional": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.9", - "extend": "3.0.1", - "http-proxy-agent": "1.0.0", - "https-proxy-agent": "1.0.0", - "lru-cache": "2.6.5", - "pac-proxy-agent": "1.1.0", - "socks-proxy-agent": "2.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz", - "integrity": "sha1-5W1jVBSO3o13B7WNFDIg/QjfD9U=", - "dev": true, - "optional": true - } - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.6" - } - }, - "pump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" - } - }, - "pumpify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", - "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", - "dev": true, - "requires": { - "duplexify": "3.5.3", - "inherits": "2.0.3", - "pump": "2.0.1" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" - } - } - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qjobs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", - "dev": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", - "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "randomfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz", - "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==", - "dev": true, - "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.1" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - } - }, - "raw-loader": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", - "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", - "dev": true - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-only-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", - "dev": true, - "requires": { - "readable-stream": "2.3.4" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - } - } - }, - "readable-stream": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", - "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.4", - "set-immediate-shim": "1.0.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" - } - }, - "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", - "dev": true, - "optional": true, - "requires": { - "double-ended-queue": "2.1.0-0", - "redis-commands": "1.3.1", - "redis-parser": "2.6.0" - } - }, - "redis-commands": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz", - "integrity": "sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs=", - "dev": true, - "optional": true - }, - "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", - "dev": true, - "optional": true - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reflect-metadata": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", - "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==", - "dev": true - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regex-not": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", - "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1" - } - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", - "dev": true, - "requires": { - "css-select": "1.2.0", - "dom-converter": "0.1.4", - "htmlparser2": "3.3.0", - "strip-ansi": "3.0.1", - "utila": "0.3.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "requestretry": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", - "dev": true, - "optional": true, - "requires": { - "extend": "3.0.1", - "lodash": "4.17.5", - "request": "2.81.0", - "when": "3.7.8" - }, - "dependencies": { - "when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", - "dev": true, - "optional": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "0.1.4" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", - "dev": true, - "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" - } - }, - "roboto-fontface": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.8.0.tgz", - "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "1.2.0" - } - }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, - "rxjs": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", - "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", - "requires": { - "symbol-observable": "1.0.1" - } - }, - "rxjs-websockets": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rxjs-websockets/-/rxjs-websockets-4.0.0.tgz", - "integrity": "sha512-b9mdl9S81FBc33R6Ywf1ePMJ+jNKt9ebrDS5+h2TH80Gp8okj5Fbydo36dQ4Julq1Re0DNmXhXEMYlEiJYJz3g==" - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", - "dev": true, - "optional": true, - "requires": { - "glob": "7.1.2", - "lodash": "4.17.5", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" - } - }, - "sass-loader": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz", - "integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==", - "dev": true, - "requires": { - "async": "2.6.0", - "clone-deep": "0.3.0", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "pify": "3.0.0" - } - }, - "saucelabs": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.3.0.tgz", - "integrity": "sha1-0kDoAJ33+ocwbsRXimm6O1xCT+4=", - "dev": true, - "requires": { - "https-proxy-agent": "1.0.0" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "5.5.2" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "optional": true, - "requires": { - "js-base64": "2.4.3", - "source-map": "0.4.4" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selenium-webdriver": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", - "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", - "dev": true, - "requires": { - "adm-zip": "0.4.7", - "rimraf": "2.6.2", - "tmp": "0.0.30", - "xml2js": "0.4.19" - }, - "dependencies": { - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - } - } - }, - "selfsigned": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.2.tgz", - "integrity": "sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=", - "dev": true, - "requires": { - "node-forge": "0.7.1" - } - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "5.5.0" - } - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" - }, - "dependencies": { - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", - "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "1.0.3", - "http-errors": "1.6.2", - "mime-types": "2.1.17", - "parseurl": "1.3.2" - } - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", - "dev": true, - "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "dev": true, - "requires": { - "to-object-path": "0.3.0" - } - }, - "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", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", - "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "shallow-clone": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", - "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", - "dev": true, - "requires": { - "is-extendable": "0.1.1", - "kind-of": "2.0.1", - "lazy-cache": "0.2.7", - "mixin-object": "2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", - "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "shasum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", - "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", - "dev": true, - "requires": { - "json-stable-stringify": "0.0.1", - "sha.js": "2.4.10" - }, - "dependencies": { - "json-stable-stringify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", - "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shell-quote": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", - "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", - "dev": true, - "requires": { - "array-filter": "0.0.1", - "array-map": "0.0.0", - "array-reduce": "0.0.0", - "jsonify": "0.0.0" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "silent-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/silent-error/-/silent-error-1.1.0.tgz", - "integrity": "sha1-IglwbxyFCp8dENDYQJGLRvJuG8k=", - "dev": true, - "requires": { - "debug": "2.6.9" - } - }, - "slack-node": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/slack-node/-/slack-node-0.2.0.tgz", - "integrity": "sha1-3kuN3aqLeT9h29KTgQT9q/N9+jA=", - "dev": true, - "optional": true, - "requires": { - "requestretry": "1.13.0" - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true - }, - "smtp-connection": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz", - "integrity": "sha1-1275EnyyPCJZ7bHoNJwujV4tdME=", - "dev": true, - "requires": { - "httpntlm": "1.6.1", - "nodemailer-shared": "1.1.0" - } - }, - "snapdragon": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", - "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", - "dev": true, - "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "socket.io": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", - "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", - "dev": true, - "requires": { - "debug": "2.6.9", - "engine.io": "3.1.4", - "socket.io-adapter": "1.1.1", - "socket.io-client": "2.0.4", - "socket.io-parser": "3.1.2" - } - }, - "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", - "dev": true - }, - "socket.io-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", - "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.6.9", - "engine.io-client": "3.1.4", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "3.1.2", - "to-array": "0.1.4" - } - }, - "socket.io-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", - "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "2.6.9", - "has-binary2": "1.0.2", - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "0.10.0", - "uuid": "3.2.1" - } - }, - "sockjs-client": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", - "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "eventsource": "0.1.6", - "faye-websocket": "0.11.1", - "inherits": "2.0.3", - "json3": "3.3.2", - "url-parse": "1.2.0" - }, - "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - } - } - }, - "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" - } - }, - "socks-proxy-agent": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz", - "integrity": "sha512-sFtmYqdUK5dAMh85H0LEVFUCO7OhJJe1/z2x/Z6mxp3s7/QPf1RkZmpZy+BpuU0bEjcV9npqKjq9Y3kwFUjnxw==", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "extend": "3.0.1", - "socks": "1.1.10" - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "1.1.0" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", - "dev": true, - "requires": { - "atob": "2.0.3", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" - } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "1.2.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", - "dev": true, - "requires": { - "debug": "2.6.9", - "handle-thing": "1.2.5", - "http-deceiver": "1.2.7", - "safe-buffer": "5.1.1", - "select-hose": "2.0.0", - "spdy-transport": "2.0.20" - } - }, - "spdy-transport": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", - "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", - "dev": true, - "requires": { - "debug": "2.6.9", - "detect-node": "2.0.3", - "hpack.js": "2.1.6", - "obuf": "1.1.1", - "readable-stream": "2.3.4", - "safe-buffer": "5.1.1", - "wbuf": "1.7.2" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "dev": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "ssri": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.2.2.tgz", - "integrity": "sha512-hm46mN8YSzjGuJtVocXPjwo0yTRXobXqYuK/tV6gr557/tRck4yWXKPRW8OxyJgRvcL3QgX+5ng/kMHBMco7KA==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true - }, - "stdout-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "2.3.4" - } - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.4" - } - }, - "stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "requires": { - "duplexer2": "0.1.4", - "readable-stream": "2.3.4" - } - }, - "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "stream-shift": "1.0.0" - } - }, - "stream-http": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", - "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.4", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "stream-splicer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", - "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.4" - } - }, - "streamroller": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", - "dev": true, - "requires": { - "date-format": "1.2.0", - "debug": "3.1.0", - "mkdirp": "0.5.1", - "readable-stream": "2.3.4" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "style-loader": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.13.2.tgz", - "integrity": "sha1-dFMzhM9pjHEEx5URULSXF63C87s=", - "dev": true, - "requires": { - "loader-utils": "1.1.0" - } - }, - "stylus": { - "version": "0.54.5", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", - "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", - "dev": true, - "requires": { - "css-parse": "1.7.0", - "debug": "2.6.9", - "glob": "7.0.6", - "mkdirp": "0.5.1", - "sax": "0.5.8", - "source-map": "0.1.43" - }, - "dependencies": { - "glob": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", - "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", - "dev": true - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "stylus-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.1.tgz", - "integrity": "sha1-d/SzT9Aw0lsmF7z1UT21sHMMQIk=", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "lodash.clonedeep": "4.5.0", - "when": "3.6.4" - } - }, - "subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, - "syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dev": true, - "requires": { - "acorn-node": "1.3.0" - } - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", - "dev": true - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "optional": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.4", - "xtend": "4.0.1" - } - }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "dev": true, - "optional": true - }, - "thunky": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", - "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", - "dev": true - }, - "time-stamp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", - "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", - "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", - "dev": true, - "requires": { - "setimmediate": "1.0.5" - } - }, - "timespan": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", - "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=", - "dev": true, - "optional": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "to-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", - "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "regex-not": "1.0.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - } - } - }, - "toposort": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.6.tgz", - "integrity": "sha1-wxdI5V0hDv/AD9zcfW5o19e7nOw=", - "dev": true - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "dev": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tree-kill": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", - "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "true-case-path": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", - "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", - "dev": true, - "optional": true, - "requires": { - "glob": "6.0.4" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "dev": true, - "optional": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - } - } - }, - "ts-node": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-4.1.0.tgz", - "integrity": "sha512-xcZH12oVg9PShKhy3UHyDmuDLV3y7iKwX25aMVPt1SIXSuAfWkFiGPEkg+th8R4YKW/QCxDoW7lJdb15lx6QWg==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "chalk": "2.3.1", - "diff": "3.4.0", - "make-error": "1.3.4", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map-support": "0.5.3", - "tsconfig": "7.0.0", - "v8flags": "3.0.1", - "yn": "2.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", - "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", - "dev": true, - "requires": { - "source-map": "0.6.1" - } - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "requires": { - "@types/strip-bom": "3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "tsickle": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.26.0.tgz", - "integrity": "sha512-eWJ2CUfttGK0LqF9iJ/Avnxbj4M+fCyJ50Zag3wm73Fut1hsasPRHKxKdrMWVj4BMHnQNx7TO+DdNmLmJTSuNw==", - "dev": true, - "requires": { - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map": "0.5.7", - "source-map-support": "0.4.18" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==" - }, - "tslint": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", - "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "builtin-modules": "1.1.1", - "chalk": "2.3.1", - "commander": "2.14.1", - "diff": "3.4.0", - "glob": "7.1.2", - "js-yaml": "3.7.0", - "minimatch": "3.0.4", - "resolve": "1.5.0", - "semver": "5.5.0", - "tslib": "1.9.0", - "tsutils": "2.21.1" - }, - "dependencies": { - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "tsscmp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", - "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=", - "dev": true, - "optional": true - }, - "tsutils": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.21.1.tgz", - "integrity": "sha512-heMkdeQ9iUc90ynfiNo5Y+GXrEEGy86KMvnSTfHO+Q40AuNQ1lZGXcv58fuU9XTUxI0V7YIN9xPN+CO9b1Gn3w==", - "dev": true, - "requires": { - "tslib": "1.9.0" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2" - } - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.17" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz", - "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==", - "dev": true - }, - "uglify-js": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.11.tgz", - "integrity": "sha512-AKLsYcdV+sS5eAE4NtVXF6f2u/DCQynQm0jTGxF261+Vltu1dYNuHzjqDmk11gInj+H/zJIM2EAwXG3MzPb3VA==", - "dev": true, - "requires": { - "commander": "2.14.1", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.8.tgz", - "integrity": "sha512-XG8/QmR1pyPeE1kj2aigo5kos8umefB31zW+PMvAAytHSB0T/vQvN6sqt8+Sh+y0b0A7zlmxNi2dzRnj0wcqGA==", - "dev": true, - "requires": { - "cacache": "10.0.2", - "find-cache-dir": "1.0.0", - "schema-utils": "0.4.5", - "serialize-javascript": "1.4.0", - "source-map": "0.6.1", - "uglify-es": "3.3.9", - "webpack-sources": "1.1.0", - "worker-farm": "1.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", - "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", - "dev": true, - "requires": { - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "6.1.1", - "ajv-keywords": "3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" - } - } - } - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, - "umd": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", - "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=", - "dev": true - }, - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" - }, - "dependencies": { - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "0.2.8" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", - "dev": true, - "requires": { - "unique-slug": "2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "dev": true, - "requires": { - "imurmurhash": "0.1.4" - } - }, - "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "upath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.2.tgz", - "integrity": "sha512-fCmij7T5LnwUme3dbnVSejvOHHlARjB3ikJFwgZfz386pHmf/gueuTLRFU94FZEaeCLlbQrweiUU700gG41tUw==", - "dev": true, - "requires": { - "lodash.endswith": "4.2.1", - "lodash.isfunction": "3.0.9", - "lodash.isstring": "4.0.1", - "lodash.startswith": "4.2.1" - } - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-loader": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", - "integrity": "sha512-h3qf9TNn53BpuXTTcpC+UehiRrl0Cv45Yr/xWayApjw6G8Bg2dGke7rIwDQ39piciWCWrC+WiqLjOh3SUp9n0Q==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "mime": "1.6.0", - "schema-utils": "0.3.0" - } - }, - "url-parse": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", - "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", - "dev": true, - "requires": { - "querystringify": "1.0.0", - "requires-port": "1.0.0" - }, - "dependencies": { - "querystringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", - "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", - "dev": true - } - } - }, - "use": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", - "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "isobject": "3.0.1", - "lazy-cache": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dev": true, - "requires": { - "set-getter": "0.1.0" - } - } - } - }, - "useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "dev": true, - "requires": { - "lru-cache": "4.1.1", - "tmp": "0.0.33" - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - }, - "uws": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", - "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=", - "dev": true, - "optional": true - }, - "v8flags": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", - "integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=", - "dev": true, - "requires": { - "homedir-polyfill": "1.0.1" - } - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "watchpack": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", - "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", - "dev": true, - "requires": { - "async": "2.6.0", - "chokidar": "1.7.0", - "graceful-fs": "4.1.11" - } - }, - "wbuf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", - "integrity": "sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=", - "dev": true, - "requires": { - "minimalistic-assert": "1.0.0" - } - }, - "web-animations-js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz", - "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA=" - }, - "webdriver-js-extender": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", - "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", - "dev": true, - "requires": { - "@types/selenium-webdriver": "2.53.43", - "selenium-webdriver": "2.53.3" - }, - "dependencies": { - "adm-zip": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", - "integrity": "sha1-ph7VrmkFw66lizplfSUDMJEFJzY=", - "dev": true - }, - "sax": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", - "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=", - "dev": true - }, - "selenium-webdriver": { - "version": "2.53.3", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", - "integrity": "sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU=", - "dev": true, - "requires": { - "adm-zip": "0.4.4", - "rimraf": "2.6.2", - "tmp": "0.0.24", - "ws": "1.1.5", - "xml2js": "0.4.4" - } - }, - "tmp": { - "version": "0.0.24", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", - "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=", - "dev": true - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "ws": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", - "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", - "dev": true, - "requires": { - "options": "0.0.6", - "ultron": "1.0.2" - } - }, - "xml2js": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", - "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", - "dev": true, - "requires": { - "sax": "0.6.1", - "xmlbuilder": "9.0.7" - } - } - } - }, - "webpack": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz", - "integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==", - "dev": true, - "requires": { - "acorn": "5.4.1", - "acorn-dynamic-import": "2.0.2", - "ajv": "5.5.2", - "ajv-keywords": "2.1.1", - "async": "2.6.0", - "enhanced-resolve": "3.4.1", - "escope": "3.6.0", - "interpret": "1.1.0", - "json-loader": "0.5.7", - "json5": "0.5.1", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "mkdirp": "0.5.1", - "node-libs-browser": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.5.0", - "tapable": "0.2.8", - "uglifyjs-webpack-plugin": "0.4.6", - "watchpack": "1.4.0", - "webpack-sources": "1.1.0", - "yargs": "8.0.2" - }, - "dependencies": { - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "2.3.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - } - }, - "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": { - "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 - }, - "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" - } - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "requires": { - "source-map": "0.5.7", - "uglify-js": "2.8.29", - "webpack-sources": "1.1.0" - } - }, - "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 - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - } - } - }, - "webpack-core": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", - "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", - "dev": true, - "requires": { - "source-list-map": "0.1.8", - "source-map": "0.4.4" - }, - "dependencies": { - "source-list-map": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", - "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "webpack-dev-middleware": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", - "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", - "dev": true, - "requires": { - "memory-fs": "0.4.1", - "mime": "1.6.0", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "time-stamp": "2.0.0" - } - }, - "webpack-dev-server": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz", - "integrity": "sha512-ombhu5KsO/85sVshIDTyQ5HF3xjZR3N0sf5Ao6h3vFwpNyzInEzA1GV3QPVjTMLTNckp8PjfG1PFGznzBwS5lg==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "array-includes": "3.0.3", - "bonjour": "3.5.0", - "chokidar": "2.0.2", - "compression": "1.7.1", - "connect-history-api-fallback": "1.5.0", - "debug": "3.1.0", - "del": "3.0.0", - "express": "4.16.2", - "html-entities": "1.2.1", - "http-proxy-middleware": "0.17.4", - "import-local": "1.0.0", - "internal-ip": "1.2.0", - "ip": "1.1.5", - "killable": "1.0.0", - "loglevel": "1.6.1", - "opn": "5.1.0", - "portfinder": "1.0.13", - "selfsigned": "1.10.2", - "serve-index": "1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.1.4", - "spdy": "3.4.7", - "strip-ansi": "3.0.1", - "supports-color": "5.2.0", - "webpack-dev-middleware": "1.12.2", - "yargs": "6.6.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "3.1.5", - "normalize-path": "2.1.1" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", - "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", - "dev": true, - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.1", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.1" - } - }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "chokidar": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.2.tgz", - "integrity": "sha512-l32Hw3wqB0L2kGVmSbK/a+xXLDrUEsc84pSgMkmwygHvD7ubRsP/vxxHa5BtB6oix1XLLVCHyYMsckRXxThmZw==", - "dev": true, - "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.0", - "fsevents": "1.1.3", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.0.2" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.5.tgz", - "integrity": "sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.0", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.7", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - }, - "yargs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", - "dev": true, - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "4.2.1" - } - }, - "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", - "dev": true, - "requires": { - "camelcase": "3.0.0" - } - } - } - }, - "webpack-merge": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.1.tgz", - "integrity": "sha512-geQsZ86YkXOVOjvPC5yv3JSNnL6/X3Kzh935AQ/gJNEYXEfJDQFu/sdFuktS9OW2JcH/SJec8TGfRdrpHshH7A==", - "dev": true, - "requires": { - "lodash": "4.17.5" - } - }, - "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", - "dev": true, - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-subresource-integrity": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.0.4.tgz", - "integrity": "sha1-j6yKfo61n8ahZ2ioXJ2U7n+dDts=", - "dev": true, - "requires": { - "webpack-core": "0.6.9" - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": "0.4.10", - "websocket-extensions": "0.1.3" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", - "dev": true - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "requires": { - "string-width": "1.0.2" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "worker-farm": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.2.tgz", - "integrity": "sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==", - "dev": true, - "requires": { - "errno": "0.1.7", - "xtend": "4.0.1" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.1" - } - }, - "xhr2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", - "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" - }, - "xml-char-classes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", - "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=", - "dev": true - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, - "requires": { - "sax": "1.2.4", - "xmlbuilder": "9.0.7" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "dev": true - }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true - }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "dev": true, - "optional": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dev": true, - "requires": { - "cuint": "0.2.2" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true - } - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true - } - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", - "dev": true - }, - "zone.js": { - "version": "0.8.20", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.20.tgz", - "integrity": "sha512-FXlA37ErSXCMy5RNBcGFgCI/Zivqzr0D19GuvDxhcYIJc7xkFp6c29DKyODJu0Zo+EMyur/WPPgcBh1EHjB9jA==" - } - } -} +{ + "name": "openems-ui", + "version": "2018.3.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/build-optimizer": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.42.tgz", + "integrity": "sha512-BAYCVZ10ro6mgZQDZiNiVbX8ppygw4q7z/stpwG8WjMswgMRIcxsxYoC1VFuWcUPAf4UyfTIav6e8UZWA5+xnQ==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "source-map": "0.5.7", + "typescript": "2.6.2", + "webpack-sources": "1.1.0" + }, + "dependencies": { + "typescript": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.29.tgz", + "integrity": "sha512-jtUBA0pIrkdXcVqDmDrGlniqwM7NFOKdo7vWFDmCVLBbC9rZHeYW5Xv/+4HyBhGLJ4wxsAkUjsHKWGJINPPpiw==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "chokidar": "1.7.0", + "rxjs": "5.5.6", + "source-map": "0.5.7" + } + }, + "@angular-devkit/schematics": { + "version": "0.0.52", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.52.tgz", + "integrity": "sha512-NtG8VB5aWtg0cw1Y7EJinJMuAnXsNdkQkkVe/i7CO6TPLyFQSFQCN1YojCr43l8jTWTRebRslrBawPCMOxsOgw==", + "dev": true, + "requires": { + "@ngtools/json-schema": "1.1.0", + "rxjs": "5.5.6" + } + }, + "@angular/animations": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.2.5.tgz", + "integrity": "sha512-70ElCmaeDxLQc2OkgYhJjXj4zjtdjI4K1D5ZZm/uSPLlUcqC6uf6skCXlhMawQoPbsL/SXE5xw2HlMgEbhUysw==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/cdk": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.2.1.tgz", + "integrity": "sha512-8vsHeRymM+p82JeBzanrjmxp0koTU5W8cXO05ojECRsj6gUE/C950rMfFDga7fC8Pu5KTru/hWQoOcKErb3Uzg==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/cli": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.6.7.tgz", + "integrity": "sha512-TprSjnQrEdrTsCAB5K/lCLuXZUH/y+l/BAR0aZLpubpZP8Ldgmq7q56trxL5wNSs3o6A8Vh43ZKNYOuKtnzlXQ==", + "dev": true, + "requires": { + "@angular-devkit/build-optimizer": "0.0.42", + "@angular-devkit/core": "0.0.29", + "@angular-devkit/schematics": "0.0.52", + "@ngtools/json-schema": "1.1.0", + "@ngtools/webpack": "1.9.7", + "@schematics/angular": "0.1.17", + "autoprefixer": "7.2.6", + "chalk": "2.2.2", + "circular-dependency-plugin": "4.4.0", + "common-tags": "1.7.2", + "copy-webpack-plugin": "4.4.1", + "core-object": "3.1.5", + "css-loader": "0.28.9", + "cssnano": "3.10.0", + "denodeify": "1.2.1", + "ember-cli-string-utils": "1.1.0", + "exports-loader": "0.6.4", + "extract-text-webpack-plugin": "3.0.2", + "file-loader": "1.1.6", + "fs-extra": "4.0.3", + "glob": "7.1.2", + "html-webpack-plugin": "2.30.1", + "istanbul-instrumenter-loader": "3.0.0", + "karma-source-map-support": "1.2.0", + "less": "2.7.3", + "less-loader": "4.0.5", + "license-webpack-plugin": "1.1.1", + "loader-utils": "1.1.0", + "lodash": "4.17.5", + "memory-fs": "0.4.1", + "minimatch": "3.0.4", + "node-modules-path": "1.0.1", + "node-sass": "4.7.2", + "nopt": "4.0.1", + "opn": "5.1.0", + "portfinder": "1.0.13", + "postcss-import": "11.1.0", + "postcss-loader": "2.1.0", + "postcss-url": "7.3.0", + "raw-loader": "0.5.1", + "resolve": "1.5.0", + "rxjs": "5.5.6", + "sass-loader": "6.0.6", + "semver": "5.5.0", + "silent-error": "1.1.0", + "source-map-support": "0.4.18", + "style-loader": "0.13.2", + "stylus": "0.54.5", + "stylus-loader": "3.0.1", + "uglifyjs-webpack-plugin": "1.1.8", + "url-loader": "0.6.2", + "webpack": "3.10.0", + "webpack-dev-middleware": "1.12.2", + "webpack-dev-server": "2.11.1", + "webpack-merge": "4.1.1", + "webpack-sources": "1.1.0", + "webpack-subresource-integrity": "1.0.4" + } + }, + "@angular/common": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.5.tgz", + "integrity": "sha512-jagCxo+75pcTwjuO1ZheIiTlKBJ6REFKFWoUPTzaSS6fnzReFJ+VPf4Pb0bWtHL1lWvbvnzmITOJPB9wmuM3fg==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/compiler": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.5.tgz", + "integrity": "sha512-YU/r5omexkrrBF3bZaseWrc2Iotk6hIdUWkPIL3gPC0hKJ3wBeB3sHCBujPQXktWdMBbQRujNSMZtgra3Oh1xQ==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/compiler-cli": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.2.5.tgz", + "integrity": "sha512-jRFMxUKpodzOBKdZc6OMse+CjK6xfTJssZQrYeIyqz2daobaIsMZP2hZX8s/PCfV8Vxa7XFwCJb7Fq2uyZKfHg==", + "dev": true, + "requires": { + "chokidar": "1.7.0", + "minimist": "1.2.0", + "reflect-metadata": "0.1.12", + "tsickle": "0.26.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "@angular/core": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.5.tgz", + "integrity": "sha512-Uo7R3LrsvA24JkRbwXWUZWp7NSEpwdTUxT1NScyjrBK+t8ybSL5/42Jo21md5M4pjeCsIgUXlGoCm1QtT5aYnQ==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/flex-layout": { + "version": "2.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.12.tgz", + "integrity": "sha512-QTOKZxehYTh8fj64V/pNVWNbfNtebSbssyMIXiGJuHTzfyF7GYdRmtjoR2pNpllycz3rE5NYX77EB140Y6BCnw==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/forms": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.5.tgz", + "integrity": "sha512-3feqqTuv9rIu7ZOsLCtM/ugNFz5RPujLHkE8bU1gsMM4/eMYruIFir2vbjnhMkD3K6KptEg4iO6tDW18diwXug==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/http": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-5.2.5.tgz", + "integrity": "sha512-VqTCkAnebe+M9Bqrfp1QYpBQCTbXide/NxrQfwiJY87kjKFeRBuy9/XH/2S5wIwlF5Yx3bmlaIufd9VI5r/0aQ==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/language-service": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-5.2.5.tgz", + "integrity": "sha512-UWNbECu8svXmrgbTL03Fr+Dn06aPCZZLScmCOGVT5lkdsiJPAJpWAvKVM2Y0nzH0PmvekHw7INtV5lwfJOijYQ==", + "dev": true + }, + "@angular/material": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.2.1.tgz", + "integrity": "sha512-94VmxclpIwXAxeudz9AfMg0m46/TEx/GsDZ7R9yOtrbptAr9xSgOumiEqET4Xjb35/mzgD/PKqlcMWyHJCkyVQ==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/platform-browser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.5.tgz", + "integrity": "sha512-iPAuoG/c3pD3hnk1g0VgJu/pzNITvLQyT0W71MDMSuxLxs291kq+U2jklm40pStISd1mPbCNKmvz/7M+WbdLhg==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.5.tgz", + "integrity": "sha512-IMEe2qUTC3CA3KoswmJJs+O2Lkyd5GXgl5ULupqhhm/TOL2FLk00kwv8k3Epaf2d1wXcjK3BMG7aAwc6RLH7QA==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/platform-server": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-5.2.5.tgz", + "integrity": "sha512-IpuEDNyoVfGO94jd1s+4IgoTBkWigwqD4YQTpcsC1mdY2Ax7NXXTAx28ZQF5EvPbSxsHGB5zG3oR7KE7GMNhYQ==", + "requires": { + "domino": "1.0.30", + "tslib": "1.9.0", + "xhr2": "0.1.4" + } + }, + "@angular/router": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-5.2.5.tgz", + "integrity": "sha512-I8U0iy59lz0dAxU4zxRQHagfUPWF+MikLNMirRL1lrA49PG+5K1tiuIQ6p+8fZFAJ5UXwNHyXqYuWqsKRiVBHQ==", + "requires": { + "tslib": "1.9.0" + } + }, + "@ngtools/json-schema": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.1.0.tgz", + "integrity": "sha1-w6DFRNYjkqzCgTpCyKDcb1j4aSI=", + "dev": true + }, + "@ngtools/webpack": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.9.7.tgz", + "integrity": "sha512-D5QuaT9wENeM2j9g2qvW9Ls1tGqRz26Lp+jxwb2ZGFep7Ik1fFOX3ROLfgkxNlxZGVmbxJjsfrYUCyGlzj8gWg==", + "dev": true, + "requires": { + "chalk": "2.2.2", + "enhanced-resolve": "3.4.1", + "loader-utils": "1.1.0", + "magic-string": "0.22.4", + "semver": "5.5.0", + "source-map": "0.5.7", + "tree-kill": "1.2.0", + "webpack-sources": "1.1.0" + } + }, + "@ngx-translate/core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-9.1.1.tgz", + "integrity": "sha1-rhA5KINrip4Gn9Li52+iGYzH5ig=" + }, + "@schematics/angular": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.1.17.tgz", + "integrity": "sha512-PHE5gk/ogPY/aN94dbbtauHMCq+/7w4Kdcl7tGmSS8mPKEI0wa6XJi//Wq/tHi55lb2fP58oEZU6n6w/wQascw==", + "dev": true, + "requires": { + "typescript": "2.6.2" + }, + "dependencies": { + "typescript": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "dev": true + } + } + }, + "@types/d3": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-4.12.0.tgz", + "integrity": "sha512-F5lVj6c2G/WPbKFk4ZVxTS8F/6IRknWcheswQcycMjBh17iJ+bRfNUtn0yvtIHtRPQSanI9Dx2U8rSSA/I+ecQ==", + "requires": { + "@types/d3-array": "1.2.1", + "@types/d3-axis": "1.0.9", + "@types/d3-brush": "1.0.7", + "@types/d3-chord": "1.0.6", + "@types/d3-collection": "1.0.5", + "@types/d3-color": "1.0.5", + "@types/d3-dispatch": "1.0.5", + "@types/d3-drag": "1.2.0", + "@types/d3-dsv": "1.0.31", + "@types/d3-ease": "1.0.7", + "@types/d3-force": "1.1.0", + "@types/d3-format": "1.2.1", + "@types/d3-geo": "1.9.4", + "@types/d3-hierarchy": "1.1.0", + "@types/d3-interpolate": "1.1.6", + "@types/d3-path": "1.0.6", + "@types/d3-polygon": "1.0.5", + "@types/d3-quadtree": "1.0.5", + "@types/d3-queue": "3.0.5", + "@types/d3-random": "1.1.0", + "@types/d3-request": "1.0.2", + "@types/d3-scale": "2.0.0", + "@types/d3-selection": "1.3.0", + "@types/d3-shape": "1.2.2", + "@types/d3-time": "1.0.7", + "@types/d3-time-format": "2.1.0", + "@types/d3-timer": "1.0.6", + "@types/d3-transition": "1.1.1", + "@types/d3-voronoi": "1.1.7", + "@types/d3-zoom": "1.7.0" + } + }, + "@types/d3-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.1.tgz", + "integrity": "sha512-YBaAfimGdWE4nDuoGVKsH89/dkz2hWZ0i8qC+xxqmqi+XJ/aXiRF0jPtzXmN7VdkpVjy1xuDmM5/m1FNuB6VWA==" + }, + "@types/d3-axis": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.9.tgz", + "integrity": "sha512-fNUnI2a0F3xiE/nGrTdDpZG4sdcRIB4X59p9jgY8O7RQiKrVqyb433YCCYSqVID4CVyoq5v3bSFliUEk0FOMsw==", + "requires": { + "@types/d3-selection": "1.3.0" + } + }, + "@types/d3-brush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.0.7.tgz", + "integrity": "sha1-BcMEQPTVN/0j+Xaw5sS6IjAB70U=", + "requires": { + "@types/d3-selection": "1.3.0" + } + }, + "@types/d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha1-BYnrl6MZH07a8Xt73kmEYokM4ew=" + }, + "@types/d3-collection": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.5.tgz", + "integrity": "sha1-ux86qXzcjYgWRVQbnWz4ft/um8M=" + }, + "@types/d3-color": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.0.5.tgz", + "integrity": "sha1-ytdV8Pxt57cPpuXgivqB70wiSN4=" + }, + "@types/d3-dispatch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.5.tgz", + "integrity": "sha1-8fkYe1OOywUVdWnY3C9w37BPG1I=" + }, + "@types/d3-drag": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.0.tgz", + "integrity": "sha512-AePmm0sXj0Tpl0uQWvwmbAf1QR3yCy9aRhjJ9mRDDSZlHBdY0SCpUtdZC9uG9Q+pyHT/dEt1R2FT/sj+5k/bVA==", + "requires": { + "@types/d3-selection": "1.3.0" + } + }, + "@types/d3-dsv": { + "version": "1.0.31", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.31.tgz", + "integrity": "sha512-UCAVZdwd2NkrbkF1lZu9vzTlmUENRRrPCubyhDPlG8Ye1B8Xr2PNvk/Tp8tMm6sPoWZWagri6/P9H+t7WqkGDg==" + }, + "@types/d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha1-k6MBhovp4VBh89RDQ7GrP4rLbwk=" + }, + "@types/d3-force": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.1.0.tgz", + "integrity": "sha512-a39Uu/ltLaMpj6K0elEB1oAqhx9rlTB5X/O75uTUqyTW2CfjhPXg6hFsX1lF8oeMc29kqGJZ4g9Pf6mET25bVw==" + }, + "@types/d3-format": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.2.1.tgz", + "integrity": "sha512-dXhA9jzCbzu6Va8ZVUQ60nu6jqA5vhPhKGR4nY3lDYfjT05GyKEKuEhfwTaSTybWczY4nLEkzv9wLQCQd5+3cA==" + }, + "@types/d3-geo": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.9.4.tgz", + "integrity": "sha512-DoigJorMGGIG9K4n980zz5g1XnvhDhNy7rk/0O8KCpFPpUZ9hyAgN0ZHXhbtIelxhJhMZxwMRe2soxx/Fhx4Hg==", + "requires": { + "@types/geojson": "7946.0.1" + } + }, + "@types/d3-hierarchy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.0.tgz", + "integrity": "sha1-UPHuBShAY4A1y91KyrH8NHCQWQc=" + }, + "@types/d3-interpolate": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.1.6.tgz", + "integrity": "sha1-ZAQbFcnAMsNI2hsiuqvFn6TRYTY=", + "requires": { + "@types/d3-color": "1.0.5" + } + }, + "@types/d3-path": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.6.tgz", + "integrity": "sha512-YHW4cs+wOU9gFUzudjJs9TkrB/8GOgKhq32ZyNaZ2rzZjOhkqG486sGr9XSh4C91CcgIg1FRGoDaN29Ropx9nw==" + }, + "@types/d3-polygon": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.5.tgz", + "integrity": "sha1-Na1U7YTDnX6fElK2U1vmAL5srOI=" + }, + "@types/d3-quadtree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.5.tgz", + "integrity": "sha1-HOHmWerkUw3wyxJ/KX8XQaNnqC4=" + }, + "@types/d3-queue": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-queue/-/d3-queue-3.0.5.tgz", + "integrity": "sha1-Pky+Kv9h22oLK4xIACmeTsasyFA=" + }, + "@types/d3-random": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.0.tgz", + "integrity": "sha1-LdCPEVnHBxknDkp8g0r4XIuI0sM=" + }, + "@types/d3-request": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-request/-/d3-request-1.0.2.tgz", + "integrity": "sha1-2524FU9HgWWEcGxub3Ar5m8i9L4=", + "requires": { + "@types/d3-dsv": "1.0.31" + } + }, + "@types/d3-scale": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.0.0.tgz", + "integrity": "sha512-fFLSdP3p9qQQ3W6ouO3GBI4Qg94CSykTWVc61U8SI1V62dfBWtOigBj5voxDcOniwh9MjKzTHldMSsGJ5qAFpA==", + "requires": { + "@types/d3-time": "1.0.7" + } + }, + "@types/d3-selection": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.3.0.tgz", + "integrity": "sha512-1SJhi3kTk/SHHIE6XkHuHU2REYkbSOjkQuo3HT71FOTs8/tjeGcvtXMsX4N3kU1UE1nVG+A5pg7TSjuJ4zUN3A==" + }, + "@types/d3-shape": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.2.2.tgz", + "integrity": "sha512-Ydksrces8J5WP/NXhZ/CcDx/XZZ8b7MDX+u6WGQXwEWfmimJn9eYHiD7QR4BLe3zBiAOQmmiGAwRBKUDp5zb1g==", + "requires": { + "@types/d3-path": "1.0.6" + } + }, + "@types/d3-time": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.7.tgz", + "integrity": "sha512-X5ZQYiJIM38XygNwld4gZ++Vtw2ftgo3KOfZOY4n/sCudUxclxf/3THBvuG8UqSV+EQ0ezYjT5eyvcrrmixOWA==" + }, + "@types/d3-time-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", + "integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==" + }, + "@types/d3-timer": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.6.tgz", + "integrity": "sha1-eG1OIHMa3wOvLF32yG/ilmf+Qps=" + }, + "@types/d3-transition": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.1.tgz", + "integrity": "sha512-GHTghl0YYB8gGgbyKxVLHyAp9Na0HqsX2U7M0u0lGw4IdfEaslooykweZ8fDHW13T+KZeZAuzhbmqBZVFO+6kg==", + "requires": { + "@types/d3-selection": "1.3.0" + } + }, + "@types/d3-voronoi": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.7.tgz", + "integrity": "sha512-/dHFLK5jhXTb/W4XEQcFydVk8qlIAo85G3r7+N2fkBFw190l0R1GQ8C1VPeXBb2GfSU5GbT2hjlnE7i7UY5Gvg==" + }, + "@types/d3-zoom": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.0.tgz", + "integrity": "sha512-eIivt2ehMUXqS0guuVzRSMr5RGhO958g9EKxIJv3Z23suPnX4VQI9k1TC/bLuwKq0IWp9a1bEEcIy+PNJv9BtA==", + "requires": { + "@types/d3-interpolate": "1.1.6", + "@types/d3-selection": "1.3.0" + } + }, + "@types/geojson": { + "version": "7946.0.1", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.1.tgz", + "integrity": "sha512-BXY6tH16Snp/ZdX6cFlBD8yfEArcZemzxEGciXkMmp1/tU76oyqkxJq91JQzT8SXWzRPwj//dw0/FdCSnnT8mw==" + }, + "@types/jasmine": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.6.tgz", + "integrity": "sha512-clg9raJTY0EOo5pVZKX3ZlMjlYzVU73L71q5OV1jhE2Uezb7oF94jh4CvwrW6wInquQAdhOxJz5VDF2TLUGmmA==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz", + "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", + "dev": true, + "requires": { + "@types/jasmine": "2.8.6" + } + }, + "@types/node": { + "version": "6.0.101", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.101.tgz", + "integrity": "sha512-IQ7V3D6+kK1DArTqTBrnl3M+YgJZLw8ta8w3Q9xjR79HaJzMAoTbZ8TNzUTztrkCKPTqIstE2exdbs1FzsYLUw==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "2.53.43", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz", + "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==", + "dev": true + }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", + "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "dev": true, + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", + "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dev": true, + "requires": { + "acorn": "4.0.13" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "acorn-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", + "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", + "dev": true, + "requires": { + "acorn": "5.4.1", + "xtend": "4.0.1" + } + }, + "addressparser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", + "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=", + "dev": true, + "optional": true + }, + "adm-zip": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", + "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", + "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", + "dev": true, + "requires": { + "extend": "3.0.1", + "semver": "5.0.3" + }, + "dependencies": { + "semver": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", + "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz", + "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "amqplib": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz", + "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==", + "dev": true, + "optional": true, + "requires": { + "bitsyntax": "0.0.4", + "bluebird": "3.5.1", + "buffer-more-ints": "0.0.2", + "readable-stream": "1.1.14", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "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, + "optional": true + } + } + }, + "angular2-toaster": { + "version": "5.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/angular2-toaster/-/angular2-toaster-5.0.0-alpha.1.tgz", + "integrity": "sha512-4fG+JBLExcpVG7V/pt5Lhi3C7nDxnqKTKIJarwe41MP9hKP94hLExlWHsEN6nhG+nLIecp+LBamP3LFOjZCFMQ==" + }, + "angular2-uuid": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/angular2-uuid/-/angular2-uuid-1.1.1.tgz", + "integrity": "sha1-cvA81TK39AAy6x7PufhFc4S+lW4=" + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + }, + "dependencies": { + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + } + } + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "2.3.11", + "normalize-path": "2.1.1" + } + }, + "app-root-path": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", + "integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=", + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "1.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", + "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", + "dev": true + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.10.0" + } + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "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", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "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", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.2.tgz", + "integrity": "sha512-ufWX953VU1eIuWqxS0nRDMYlGyFH+yxln5CsmIHlpzEt3fdYqUnRtsFt0XAsQot8OaVCwFqxT1RiwvtzYjeYeg==", + "dev": true, + "optional": true + }, + "astw": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", + "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", + "dev": true, + "requires": { + "acorn": "4.0.13" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "dev": true, + "requires": { + "lodash": "4.17.5" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", + "dev": true + }, + "autoprefixer": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", + "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", + "dev": true, + "requires": { + "browserslist": "2.11.3", + "caniuse-lite": "1.0.30000808", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "6.0.18", + "postcss-value-parser": "3.3.0" + } + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "axios": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", + "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", + "dev": true, + "optional": true, + "requires": { + "follow-redirects": "1.0.0" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.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 + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.5", + "source-map": "0.5.7", + "trim-right": "1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + } + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "2.5.3", + "regenerator-runtime": "0.11.1" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.5" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.5" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.5", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", + "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "bitsyntax": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.0.4.tgz", + "integrity": "sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI=", + "dev": true, + "optional": true, + "requires": { + "buffer-more-ints": "0.0.2" + } + }, + "bl": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", + "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "2.0.6" + }, + "dependencies": { + "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, + "optional": 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, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "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, + "optional": true + } + } + }, + "blob": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "dev": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "optional": true, + "requires": { + "inherits": "2.0.3" + } + }, + "blocking-proxy": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.5.tgz", + "integrity": "sha1-RikF4Nz76pcPQao3Ij3anAexkSs=", + "dev": true, + "requires": { + "minimist": "1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + }, + "dependencies": { + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "2.1.1", + "deep-equal": "1.0.1", + "dns-equal": "1.0.0", + "dns-txt": "2.0.2", + "multicast-dns": "6.2.3", + "multicast-dns-service-types": "1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", + "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "combine-source-map": "0.8.0", + "defined": "1.0.0", + "safe-buffer": "5.1.1", + "through2": "2.0.3", + "umd": "3.0.1" + } + }, + "browser-resolve": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz", + "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "assert": "1.4.1", + "browser-pack": "6.0.4", + "browser-resolve": "1.11.2", + "browserify-zlib": "0.2.0", + "buffer": "5.1.0", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "defined": "1.0.0", + "deps-sort": "2.0.0", + "domain-browser": "1.1.7", + "duplexer2": "0.1.4", + "events": "1.1.1", + "glob": "7.1.2", + "has": "1.0.1", + "htmlescape": "1.1.1", + "https-browserify": "1.0.0", + "inherits": "2.0.3", + "insert-module-globals": "7.0.1", + "labeled-stream-splicer": "2.0.0", + "module-deps": "4.1.1", + "os-browserify": "0.3.0", + "parents": "1.0.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "read-only-stream": "2.0.0", + "readable-stream": "2.3.4", + "resolve": "1.5.0", + "shasum": "1.0.2", + "shell-quote": "1.6.1", + "stream-browserify": "2.0.1", + "stream-http": "2.8.0", + "string_decoder": "1.0.3", + "subarg": "1.0.0", + "syntax-error": "1.4.0", + "through2": "2.0.3", + "timers-browserify": "1.4.2", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4", + "xtend": "4.0.1" + }, + "dependencies": { + "buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", + "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", + "dev": true, + "requires": { + "base64-js": "1.2.3", + "ieee754": "1.1.8" + } + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "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.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "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 + } + } + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "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 + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "requires": { + "process": "0.11.10" + } + } + } + }, + "browserify-aes": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", + "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", + "dev": true, + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "dev": true, + "requires": { + "browserify-aes": "1.1.1", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.3" + } + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.3" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "randombytes": "2.0.6" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "elliptic": "6.4.0", + "inherits": "2.0.3", + "parse-asn1": "5.1.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "1.0.6" + } + }, + "browserslist": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "dev": true, + "requires": { + "caniuse-lite": "1.0.30000808", + "electron-to-chromium": "1.3.33" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "1.2.3", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-more-ints": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", + "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "buildmail": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-4.0.1.tgz", + "integrity": "sha1-h393OLeHKYccmhBeO4N9K+EaenI=", + "dev": true, + "optional": true, + "requires": { + "addressparser": "1.0.1", + "libbase64": "0.1.0", + "libmime": "3.0.0", + "libqp": "1.1.0", + "nodemailer-fetch": "1.6.0", + "nodemailer-shared": "1.1.0", + "punycode": "1.4.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.2.tgz", + "integrity": "sha512-dljb7dk1jqO5ogE+dRpoR9tpHYv5xz9vPSNunh1+0wRuNdYxmzp9WmsyokgW/DUF1FDRVA/TMsmxt027R8djbQ==", + "dev": true, + "requires": { + "bluebird": "3.5.1", + "chownr": "1.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.1", + "mississippi": "1.3.1", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.2.2", + "unique-filename": "1.1.0", + "y18n": "3.2.1" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "cached-path-relative": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", + "dev": true + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "2.3.2", + "upper-case": "1.1.3" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000808", + "lodash.memoize": "4.1.2", + "lodash.uniq": "4.5.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000808", + "electron-to-chromium": "1.3.33" + } + } + } + }, + "caniuse-db": { + "version": "1.0.30000808", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000808.tgz", + "integrity": "sha1-MN/YMAnVcE8C3/s3clBo7RKjZrs=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30000808", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000808.tgz", + "integrity": "sha512-vT0JLmHdvq1UVbYXioxCXHYdNw55tyvi+IUWyX0Zeh1OFQi2IllYtm38IJnSgHWCv/zUnX1hdhy3vMJvuTNSqw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + }, + "dependencies": { + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + } + } + }, + "chalk": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", + "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "chart.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.1.tgz", + "integrity": "sha512-pX1oQAY86MiuyZ2hY593Acbl4MLHKrBBhhmZ1YqSadzQbbsBE2rnd6WISoHjIsdf0WDeC0hbePYCz2ZxkV8L+g==", + "requires": { + "chartjs-color": "2.2.0", + "moment": "2.18.1" + } + }, + "chartjs-color": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", + "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", + "requires": { + "chartjs-color-string": "0.5.0", + "color-convert": "0.5.3" + } + }, + "chartjs-color-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", + "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", + "requires": { + "color-name": "1.1.3" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.1.3", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "circular-dependency-plugin": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-4.4.0.tgz", + "integrity": "sha512-yEFtUNUYT4jBykEX5ZOHw+5goA3glGZr9wAXIQqoyakjz5H5TeUmScnWRc52douAhb9eYzK3s7V6bXfNnjFdzg==", + "dev": true + }, + "circular-json": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.1.tgz", + "integrity": "sha512-UjgcRlTAhAkLeXmDe2wK7ktwy/tgAqxiSndTIPiFZuIPLZmzHzWMwUIe9h9m/OokypG7snxCDEuwJshGBdPvaw==", + "dev": true + }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "dev": true, + "requires": { + "chalk": "1.1.3" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.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 + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "classlist.js": { + "version": "1.1.20150312", + "resolved": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz", + "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k=" + }, + "clean-css": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", + "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "clone": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", + "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "dev": true + }, + "clone-deep": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz", + "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=", + "dev": true, + "requires": { + "for-own": "1.0.0", + "is-plain-object": "2.0.4", + "kind-of": "3.2.2", + "shallow-clone": "0.1.2" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "requires": { + "q": "1.5.1" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "codelyzer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.1.0.tgz", + "integrity": "sha512-a3FCIAS3FNQIACvj7KA4iKvH3c6r7X6t6zXsrtV797QGYPQyCwD1fIEd9yV+ZDamijF3YaZ5fbB7QbUMOJGC/g==", + "dev": true, + "requires": { + "app-root-path": "2.0.1", + "css-selector-tokenizer": "0.7.0", + "cssauron": "1.4.0", + "semver-dsl": "1.0.1", + "source-map": "0.5.7", + "sprintf-js": "1.0.3" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "requires": { + "clone": "1.0.3", + "color-convert": "1.9.1", + "color-string": "0.3.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + } + } + }, + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "requires": { + "color": "0.11.4", + "css-color-names": "0.0.4", + "has": "1.0.1" + } + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "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.17.5" + } + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + }, + "dependencies": { + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + } + } + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", + "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==" + }, + "common-tags": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz", + "integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compressible": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz", + "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "compression": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz", + "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=", + "dev": true, + "requires": { + "accepts": "1.3.4", + "bytes": "3.0.0", + "compressible": "2.0.12", + "debug": "2.6.9", + "on-headers": "1.0.1", + "safe-buffer": "5.1.1", + "vary": "1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4", + "typedarray": "0.0.6" + } + }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "1.3.2", + "utils-merge": "1.0.1" + } + }, + "connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.4.1.tgz", + "integrity": "sha512-ojaz8MpS3zoLJT/JbYMusYM+dCEArhW24hGAUPYPydTCS+87NFh2TWr85sywG3So4Q4E68QoerqQ+Ns1g0fhDg==", + "dev": true, + "requires": { + "cacache": "10.0.2", + "find-cache-dir": "1.0.0", + "globby": "7.1.1", + "is-glob": "4.0.0", + "loader-utils": "0.2.17", + "minimatch": "3.0.4", + "p-limit": "1.2.0", + "serialize-javascript": "1.4.0" + }, + "dependencies": { + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" + } + } + } + }, + "core-js": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", + "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" + }, + "core-object": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/core-object/-/core-object-3.1.5.tgz", + "integrity": "sha512-sA2/4+/PZ/KV6CKgjrVrrUVBKCkdDO02CUlQ0YKTQoYUwPYNOtOAcWlbYhd5v/1JqYaA6oZ4sDlOU4ppVw6Wbg==", + "dev": true, + "requires": { + "chalk": "2.2.2" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "requires": { + "is-directory": "0.3.1", + "js-yaml": "3.7.0", + "minimist": "1.2.0", + "object-assign": "4.1.1", + "os-homedir": "1.0.2", + "parse-json": "2.2.0", + "require-from-string": "1.2.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0" + } + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "ripemd160": "2.0.1", + "sha.js": "2.4.10" + } + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "inherits": "2.0.3", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.10" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "4.1.1", + "which": "1.3.0" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.0", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "diffie-hellman": "5.0.2", + "inherits": "2.0.3", + "pbkdf2": "3.0.14", + "public-encrypt": "4.0.0", + "randombytes": "2.0.6", + "randomfill": "1.0.3" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-loader": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.9.tgz", + "integrity": "sha512-r3dgelMm/mkPz5Y7m9SeiGE46i2VsEU/OYbez+1llfxtv8b2y5/b5StaeEvPK3S5tlNQI+tDW/xDIhKJoZgDtw==", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "css-selector-tokenizer": "0.7.0", + "cssnano": "3.10.0", + "icss-utils": "2.1.0", + "loader-utils": "1.1.0", + "lodash.camelcase": "4.3.0", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-modules-extract-imports": "1.2.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0", + "postcss-value-parser": "3.3.0", + "source-list-map": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "1.0.0", + "css-what": "2.1.0", + "domutils": "1.5.1", + "nth-check": "1.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "dev": true, + "requires": { + "cssesc": "0.1.0", + "fastparse": "1.1.1", + "regexpu-core": "1.0.0" + } + }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + "dev": true + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "requires": { + "autoprefixer": "6.7.7", + "decamelize": "1.2.0", + "defined": "1.0.0", + "has": "1.0.1", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-calc": "5.3.1", + "postcss-colormin": "2.2.2", + "postcss-convert-values": "2.6.1", + "postcss-discard-comments": "2.0.4", + "postcss-discard-duplicates": "2.1.0", + "postcss-discard-empty": "2.1.0", + "postcss-discard-overridden": "0.1.1", + "postcss-discard-unused": "2.2.3", + "postcss-filter-plugins": "2.0.2", + "postcss-merge-idents": "2.1.7", + "postcss-merge-longhand": "2.0.2", + "postcss-merge-rules": "2.1.2", + "postcss-minify-font-values": "1.0.5", + "postcss-minify-gradients": "1.0.5", + "postcss-minify-params": "1.2.2", + "postcss-minify-selectors": "2.1.1", + "postcss-normalize-charset": "1.1.1", + "postcss-normalize-url": "3.0.8", + "postcss-ordered-values": "2.2.3", + "postcss-reduce-idents": "2.4.0", + "postcss-reduce-initial": "1.0.1", + "postcss-reduce-transforms": "1.0.4", + "postcss-svgo": "2.1.6", + "postcss-unique-selectors": "2.0.2", + "postcss-value-parser": "3.3.0", + "postcss-zindex": "2.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000808", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000808", + "electron-to-chromium": "1.3.33" + } + }, + "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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "requires": { + "clap": "1.2.3", + "source-map": "0.5.7" + } + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "0.10.37" + } + }, + "d3": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz", + "integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==", + "requires": { + "d3-array": "1.2.1", + "d3-axis": "1.0.8", + "d3-brush": "1.0.4", + "d3-chord": "1.0.4", + "d3-collection": "1.0.4", + "d3-color": "1.0.3", + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-dsv": "1.0.8", + "d3-ease": "1.0.3", + "d3-force": "1.1.0", + "d3-format": "1.2.2", + "d3-geo": "1.9.1", + "d3-hierarchy": "1.1.5", + "d3-interpolate": "1.1.6", + "d3-path": "1.0.5", + "d3-polygon": "1.0.3", + "d3-quadtree": "1.0.3", + "d3-queue": "3.0.7", + "d3-random": "1.1.0", + "d3-request": "1.0.6", + "d3-scale": "1.0.7", + "d3-selection": "1.3.0", + "d3-shape": "1.2.0", + "d3-time": "1.0.8", + "d3-time-format": "2.1.1", + "d3-timer": "1.0.7", + "d3-transition": "1.1.1", + "d3-voronoi": "1.1.2", + "d3-zoom": "1.7.1" + }, + "dependencies": { + "d3-scale": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", + "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", + "requires": { + "d3-array": "1.2.1", + "d3-collection": "1.0.4", + "d3-color": "1.0.3", + "d3-format": "1.2.2", + "d3-interpolate": "1.1.6", + "d3-time": "1.0.8", + "d3-time-format": "2.1.1" + } + } + } + }, + "d3-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", + "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==" + }, + "d3-axis": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz", + "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=" + }, + "d3-brush": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", + "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", + "requires": { + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-interpolate": "1.1.6", + "d3-selection": "1.3.0", + "d3-transition": "1.1.1" + } + }, + "d3-chord": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", + "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", + "requires": { + "d3-array": "1.2.1", + "d3-path": "1.0.5" + } + }, + "d3-collection": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", + "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=" + }, + "d3-color": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", + "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=" + }, + "d3-dispatch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", + "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" + }, + "d3-drag": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", + "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", + "requires": { + "d3-dispatch": "1.0.3", + "d3-selection": "1.3.0" + } + }, + "d3-dsv": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", + "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", + "requires": { + "commander": "2.14.1", + "iconv-lite": "0.4.19", + "rw": "1.3.3" + } + }, + "d3-ease": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", + "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" + }, + "d3-force": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", + "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==", + "requires": { + "d3-collection": "1.0.4", + "d3-dispatch": "1.0.3", + "d3-quadtree": "1.0.3", + "d3-timer": "1.0.7" + } + }, + "d3-format": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz", + "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw==" + }, + "d3-geo": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz", + "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==", + "requires": { + "d3-array": "1.2.1" + } + }, + "d3-hierarchy": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", + "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=" + }, + "d3-interpolate": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz", + "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==", + "requires": { + "d3-color": "1.0.3" + } + }, + "d3-path": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", + "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=" + }, + "d3-polygon": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz", + "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI=" + }, + "d3-quadtree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", + "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=" + }, + "d3-queue": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz", + "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg=" + }, + "d3-random": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz", + "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM=" + }, + "d3-request": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz", + "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==", + "requires": { + "d3-collection": "1.0.4", + "d3-dispatch": "1.0.3", + "d3-dsv": "1.0.8", + "xmlhttprequest": "1.8.0" + } + }, + "d3-scale": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.0.0.tgz", + "integrity": "sha512-Sa2Ny6CoJT7x6dozxPnvUQT61epGWsgppFvnNl8eJEzfJBG0iDBBTJAtz2JKem7Mb+NevnaZiDiIDHsuWkv6vg==", + "requires": { + "d3-array": "1.2.1", + "d3-collection": "1.0.4", + "d3-format": "1.2.2", + "d3-interpolate": "1.1.6", + "d3-time": "1.0.8", + "d3-time-format": "2.1.1" + } + }, + "d3-selection": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz", + "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA==" + }, + "d3-shape": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", + "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", + "requires": { + "d3-path": "1.0.5" + } + }, + "d3-time": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz", + "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==" + }, + "d3-time-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", + "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", + "requires": { + "d3-time": "1.0.8" + } + }, + "d3-timer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", + "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==" + }, + "d3-transition": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", + "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", + "requires": { + "d3-color": "1.0.3", + "d3-dispatch": "1.0.3", + "d3-ease": "1.0.3", + "d3-interpolate": "1.1.6", + "d3-selection": "1.3.0", + "d3-timer": "1.0.7" + } + }, + "d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=" + }, + "d3-zoom": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz", + "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==", + "requires": { + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-interpolate": "1.1.6", + "d3-selection": "1.3.0", + "d3-transition": "1.1.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "data-uri-to-buffer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", + "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", + "dev": true, + "optional": true + }, + "date-fns": { + "version": "2.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.0-alpha.7.tgz", + "integrity": "sha1-JFrRb5V2Tqur+ywKQf1dAzwg5Xo=" + }, + "date-format": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true, + "optional": true + }, + "default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "requires": { + "strip-bom": "2.0.0" + } + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "degenerator": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", + "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", + "dev": true, + "optional": true, + "requires": { + "ast-types": "0.10.2", + "escodegen": "1.9.0", + "esprima": "3.1.3" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true, + "optional": true + } + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "6.1.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "p-map": "1.2.0", + "pify": "3.0.0", + "rimraf": "2.6.2" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "denodeify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", + "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "deps-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "shasum": "1.0.2", + "subarg": "1.0.0", + "through2": "2.0.3" + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "detect-node": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", + "dev": true + }, + "detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "dev": true, + "requires": { + "acorn": "5.4.1", + "defined": "1.0.0" + } + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", + "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.0.6" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "path-type": "3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "1.1.5", + "safe-buffer": "5.1.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "1.1.1" + } + }, + "dom-converter": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", + "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", + "dev": true, + "requires": { + "utila": "0.3.3" + }, + "dependencies": { + "utila": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", + "dev": true + } + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "1.0.1", + "ent": "2.2.0", + "extend": "3.0.1", + "void-elements": "2.0.1" + } + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "1.1.3", + "entities": "1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } + }, + "domino": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz", + "integrity": "sha512-ikq8WiDSkICdkElud317F2Sigc6A3EDpWsxWBwIZqOl95km4p/Vc9Rj98id7qKgsjDmExj0AVM7JOd4bb647Xg==" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" + } + }, + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", + "dev": true, + "optional": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "2.3.4" + } + }, + "duplexify": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz", + "integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.4", + "stream-shift": "1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", + "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz", + "integrity": "sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU=", + "dev": true + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "ember-cli-string-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ember-cli-string-utils/-/ember-cli-string-utils-1.1.0.tgz", + "integrity": "sha1-ObZ3/CgF9VFzc1N2/O8njqpEUqE=", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "engine.io": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.4.tgz", + "integrity": "sha1-PQIRtwpVLOhB/8fahiezAamkFi4=", + "dev": true, + "requires": { + "accepts": "1.3.3", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "2.6.9", + "engine.io-parser": "2.1.2", + "uws": "0.14.5", + "ws": "3.3.3" + }, + "dependencies": { + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true, + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + } + } + }, + "engine.io-client": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", + "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "2.6.9", + "engine.io-parser": "2.1.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", + "yeast": "0.1.2" + } + }, + "engine.io-parser": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz", + "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.4", + "has-binary2": "1.0.2" + } + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "memory-fs": "0.4.1", + "object-assign": "4.1.1", + "tapable": "0.2.8" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "1.0.1" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", + "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, + "es5-ext": { + "version": "0.10.37", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", + "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=", + "dev": true, + "requires": { + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.37", + "es6-symbol": "3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.37", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.37", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.37" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.37", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz", + "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==", + "dev": true, + "optional": true, + "requires": { + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.5.7" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true, + "optional": true + } + } + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true, + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.37" + } + }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "dev": true, + "requires": { + "original": "1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "1.3.4", + "safe-buffer": "5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "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.5" + }, + "dependencies": { + "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.1" + } + }, + "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": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "exports-loader": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.6.4.tgz", + "integrity": "sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY=", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "source-map": "0.5.7" + } + }, + "express": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "dev": true, + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.1", + "serve-static": "1.13.1", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "extract-text-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", + "dev": true, + "requires": { + "async": "2.6.0", + "loader-utils": "1.1.0", + "schema-utils": "0.3.0", + "webpack-sources": "1.1.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true, + "optional": true + }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, + "file-loader": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.6.tgz", + "integrity": "sha512-873ztuL+/hfvXbLDJ262PGO6XjERnybJu2gW1/5j8HUfxSiFJI9Hj/DhZ50ZGRUxBvuNiazb/cM2rh9pqrxP6Q==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "schema-utils": "0.3.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "7.1.2", + "minimatch": "3.0.4" + } + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "1.0.1", + "make-dir": "1.1.0", + "pkg-dir": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "dev": true + }, + "flush-write-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz", + "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4" + } + }, + "follow-redirects": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", + "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.17" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "1.0.0" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "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, + "requires": { + "wrappy": "1.0.2" + } + }, + "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.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "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.2.9", + "bundled": true, + "dev": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.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 + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "1.1.14", + "xregexp": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "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, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "gaze": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "dev": true, + "optional": true, + "requires": { + "globule": "1.2.0" + } + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true, + "optional": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "optional": true, + "requires": { + "is-property": "1.0.2" + } + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.1.tgz", + "integrity": "sha512-7aelVrYqCLuVjq2kEKRTH8fXPTC0xKTkM+G7UlFkEwCXY3sFbSxvY375JoFowOAYbkaU47SrBvOefUlLZZ+6QA==", + "dev": true, + "optional": true, + "requires": { + "data-uri-to-buffer": "1.2.0", + "debug": "2.6.9", + "extend": "3.0.1", + "file-uri-to-path": "1.0.0", + "ftp": "0.3.10", + "readable-stream": "2.3.4" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "glob": "7.1.2", + "ignore": "3.3.7", + "pify": "3.0.0", + "slash": "1.0.0" + } + }, + "globule": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.5", + "minimatch": "3.0.4" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, + "handle-thing": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "dev": true, + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "optional": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "optional": true + } + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + } + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-binary2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", + "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hipchat-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz", + "integrity": "sha1-ttJJdVQ3wZEII2d5nTupoPI7Ix4=", + "dev": true, + "optional": true, + "requires": { + "lodash": "4.17.5", + "request": "2.81.0" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "1.1.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" + } + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "obuf": "1.1.1", + "readable-stream": "2.3.4", + "wbuf": "1.7.2" + } + }, + "html-comment-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", + "dev": true + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "html-minifier": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.9.tgz", + "integrity": "sha512-EZqO91XJwkj8BeLx9C12sKB/AHoTANaZax39vEOP9f/X/9jgJ3r1O2+neabuHqpz5kJO71TapP9JrtCY39su1A==", + "dev": true, + "requires": { + "camel-case": "3.0.0", + "clean-css": "4.1.9", + "commander": "2.14.1", + "he": "1.1.1", + "ncname": "1.0.0", + "param-case": "2.1.1", + "relateurl": "0.2.7", + "uglify-js": "3.3.11" + } + }, + "html-webpack-plugin": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz", + "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", + "dev": true, + "requires": { + "bluebird": "3.5.1", + "html-minifier": "3.5.9", + "loader-utils": "0.2.17", + "lodash": "4.17.5", + "pretty-error": "2.1.1", + "toposort": "1.0.6" + }, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" + } + } + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.1.0", + "domutils": "1.1.6", + "readable-stream": "1.0.34" + }, + "dependencies": { + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "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 + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "dev": true + }, + "http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "dev": true, + "requires": { + "eventemitter3": "1.2.0", + "requires-port": "1.0.0" + } + }, + "http-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz", + "integrity": "sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo=", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1" + } + }, + "http-proxy-middleware": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", + "dev": true, + "requires": { + "http-proxy": "1.16.2", + "is-glob": "3.1.0", + "lodash": "4.17.5", + "micromatch": "2.3.11" + }, + "dependencies": { + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "httpntlm": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", + "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", + "dev": true, + "requires": { + "httpreq": "0.4.24", + "underscore": "1.7.0" + } + }, + "httpreq": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", + "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=", + "dev": true + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", + "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "requires": { + "postcss": "6.0.18" + } + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "2.0.0", + "resolve-cwd": "2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true, + "optional": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflection": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.10.0.tgz", + "integrity": "sha1-W//LEZetPoEFD44X4hZoCH7p6y8=", + "dev": true, + "optional": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "insert-module-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", + "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "combine-source-map": "0.7.2", + "concat-stream": "1.5.2", + "is-buffer": "1.1.6", + "lexical-scope": "1.2.0", + "process": "0.11.10", + "through2": "2.0.3", + "xtend": "4.0.1" + }, + "dependencies": { + "combine-source-map": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + } + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + } + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "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.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "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 + } + } + }, + "internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", + "dev": true, + "requires": { + "meow": "3.7.0" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "intl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz", + "integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94=" + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "1.11.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true, + "optional": true + }, + "is-my-json-valid": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", + "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "dev": true, + "optional": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "is-my-ip-valid": "1.0.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-odd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", + "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", + "dev": true, + "requires": { + "is-number": "3.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true, + "optional": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "requires": { + "html-comment-regex": "1.1.1" + } + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", + "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-api": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.2.2.tgz", + "integrity": "sha512-kH5YRdqdbs5hiH4/Rr1Q0cSAGgjh3jTtg8vu9NLebBAoK3adVO4jk81J+TYOkTr2+Q4NLeb1ACvmEt65iG/Vbw==", + "dev": true, + "requires": { + "async": "2.6.0", + "fileset": "2.0.3", + "istanbul-lib-coverage": "1.1.2", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.9.2", + "istanbul-lib-report": "1.1.3", + "istanbul-lib-source-maps": "1.2.3", + "istanbul-reports": "1.1.4", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "once": "1.4.0" + } + }, + "istanbul-instrumenter-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz", + "integrity": "sha512-alLSEFX06ApU75sm5oWcaVNaiss/bgMRiWTct3g0P0ZZTKjR+6QiCcuVOKDI1kWJgwHEnIXsv/dWm783kPpmtw==", + "dev": true, + "requires": { + "convert-source-map": "1.5.1", + "istanbul-lib-instrument": "1.9.2", + "loader-utils": "1.1.0", + "schema-utils": "0.3.0" + } + }, + "istanbul-lib-coverage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.2.tgz", + "integrity": "sha512-tZYA0v5A7qBSsOzcebJJ/z3lk3oSzH62puG78DbBA1+zupipX2CakDyiPV3pOb8He+jBwVimuwB0dTnh38hX0w==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", + "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", + "dev": true, + "requires": { + "append-transform": "0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.2.tgz", + "integrity": "sha512-nz8t4HQ2206a/3AXi+NHFWEa844DMpPsgbcUteJbt1j8LX1xg56H9rOMnhvcvVvPbW60qAIyrSk44H8ZDqaSSA==", + "dev": true, + "requires": { + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.2", + "semver": "5.5.0" + } + }, + "istanbul-lib-report": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz", + "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "1.1.2", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", + "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", + "dev": true, + "requires": { + "debug": "3.1.0", + "istanbul-lib-coverage": "1.1.2", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.4.tgz", + "integrity": "sha512-DfSTVOTkuO+kRmbO8Gk650Wqm1WRGr6lrdi2EwDK1vxpS71vdlLd613EpzOKdIFioB5f/scJTjeWBnvd1FWejg==", + "dev": true, + "requires": { + "handlebars": "4.0.11" + } + }, + "jasmine": { + "version": "2.99.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.99.0.tgz", + "integrity": "sha1-jKctEC5jm4Z8ZImFbg4YqceqQrc=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "7.1.2", + "jasmine-core": "2.99.1" + }, + "dependencies": { + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", + "dev": true, + "requires": { + "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.3", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", + "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "2.7.3" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "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.11" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "karma": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.0.tgz", + "integrity": "sha512-K9Kjp8CldLyL9ANSUctDyxC7zH3hpqXj/K09qVf06K3T/kXaHtFZ5tQciK7OzQu68FLvI89Na510kqQ2LCbpIw==", + "dev": true, + "requires": { + "bluebird": "3.5.1", + "body-parser": "1.18.2", + "browserify": "14.5.0", + "chokidar": "1.7.0", + "colors": "1.1.2", + "combine-lists": "1.0.1", + "connect": "3.6.6", + "core-js": "2.5.3", + "di": "0.0.1", + "dom-serialize": "2.2.1", + "expand-braces": "0.1.2", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "http-proxy": "1.16.2", + "isbinaryfile": "3.0.2", + "lodash": "4.17.5", + "log4js": "2.5.3", + "mime": "1.6.0", + "minimatch": "3.0.4", + "optimist": "0.6.1", + "qjobs": "1.1.5", + "range-parser": "1.2.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.1", + "socket.io": "2.0.4", + "source-map": "0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "dev": true, + "requires": { + "fs-access": "1.0.1", + "which": "1.3.0" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.1.tgz", + "integrity": "sha512-5og0toMjgLvsL9+TzGH4Rk1D0nr7pMIRJBg29xP4mHMKy/1KUJ12UzoqI6mBNCRFa4nDvZS2MRrN7p+RkZNWxQ==", + "dev": true, + "requires": { + "istanbul-api": "1.2.2", + "minimatch": "3.0.4" + } + }, + "karma-jasmine": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz", + "integrity": "sha1-b+hA51oRYAydkehLM8RY4cRqNSk=", + "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.1.1" + } + }, + "karma-source-map-support": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.2.0.tgz", + "integrity": "sha1-G/gee7SwiWJ6s1LsQXnhF8QGpUA=", + "dev": true, + "requires": { + "source-map-support": "0.4.18" + } + }, + "killable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", + "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "labeled-stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", + "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "isarray": "0.0.1", + "stream-splicer": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "less": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", + "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "dev": true, + "requires": { + "errno": "0.1.7", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.81.0", + "source-map": "0.5.7" + } + }, + "less-loader": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.0.5.tgz", + "integrity": "sha1-rhVadAbKxqzSk9eFWH/P8PR4xN0=", + "dev": true, + "requires": { + "clone": "2.1.1", + "loader-utils": "1.1.0", + "pify": "2.3.0" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "optional": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "lexical-scope": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", + "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", + "dev": true, + "requires": { + "astw": "2.2.0" + } + }, + "libbase64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", + "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=", + "dev": true + }, + "libmime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz", + "integrity": "sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY=", + "dev": true, + "requires": { + "iconv-lite": "0.4.15", + "libbase64": "0.1.0", + "libqp": "1.1.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", + "dev": true + } + } + }, + "libqp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", + "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=", + "dev": true + }, + "license-webpack-plugin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.1.1.tgz", + "integrity": "sha512-TjKOyiC0exqd4Idy/4M8/DETR22dXBZks387DuS5LbslxHiMRXGx/Q2F/j9IUtvEoH5uFvt72vRgk/G6f8j3Dg==", + "dev": true, + "requires": { + "ejs": "2.5.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true, + "optional": true + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.endswith": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.endswith/-/lodash.endswith-4.2.1.tgz", + "integrity": "sha1-/tWawXOO0+I27dcGTsRWRIs3vAk=", + "dev": true + }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "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==", + "dev": true, + "optional": true + }, + "lodash.startswith": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz", + "integrity": "sha1-xZjErc4YiiflMUVzHNxsDnF3YAw=", + "dev": true + }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log4js": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.5.3.tgz", + "integrity": "sha512-YL/qpTxYtK0iWWbuKCrevDZz5lh+OjyHHD+mICqpjnYGKdNRBvPeh/1uYjkKUemT1CSO4wwLOwphWMpKAnD9kw==", + "dev": true, + "requires": { + "amqplib": "0.5.2", + "axios": "0.15.3", + "circular-json": "0.5.1", + "date-format": "1.2.0", + "debug": "3.1.0", + "hipchat-notifier": "1.1.0", + "loggly": "1.1.1", + "mailgun-js": "0.7.15", + "nodemailer": "2.7.2", + "redis": "2.8.0", + "semver": "5.5.0", + "slack-node": "0.2.0", + "streamroller": "0.7.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "loggly": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/loggly/-/loggly-1.1.1.tgz", + "integrity": "sha1-Cg/B0/o6XsRP3HuJe+uipGlc6+4=", + "dev": true, + "optional": true, + "requires": { + "json-stringify-safe": "5.0.1", + "request": "2.75.0", + "timespan": "2.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "form-data": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz", + "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=", + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.17" + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "optional": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.14.1", + "is-my-json-valid": "2.17.2", + "pinkie-promise": "2.0.1" + } + }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", + "dev": true, + "optional": true + }, + "qs": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", + "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=", + "dev": true, + "optional": true + }, + "request": { + "version": "2.75.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", + "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "bl": "1.1.2", + "caseless": "0.11.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.0.0", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "node-uuid": "1.4.8", + "oauth-sign": "0.8.2", + "qs": "6.2.3", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.4.3" + } + }, + "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, + "optional": true + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true, + "optional": true + } + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "macaddress": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", + "dev": true + }, + "magic-string": { + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", + "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", + "dev": true, + "requires": { + "vlq": "0.2.3" + } + }, + "mailcomposer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", + "integrity": "sha1-DhxEsqB890DuF9wUm6AJ8Zyt/rQ=", + "dev": true, + "optional": true, + "requires": { + "buildmail": "4.0.1", + "libmime": "3.0.0" + } + }, + "mailgun-js": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.7.15.tgz", + "integrity": "sha1-7jZqINrGTDwVwD1sGz4O15UlKrs=", + "dev": true, + "optional": true, + "requires": { + "async": "2.1.5", + "debug": "2.2.0", + "form-data": "2.1.4", + "inflection": "1.10.0", + "is-stream": "1.1.0", + "path-proxy": "1.0.0", + "proxy-agent": "2.0.0", + "q": "1.4.1", + "tsscmp": "1.0.5" + }, + "dependencies": { + "async": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.5.tgz", + "integrity": "sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw=", + "dev": true, + "optional": true, + "requires": { + "lodash": "4.17.5" + } + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "optional": true, + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true, + "optional": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true, + "optional": true + } + } + }, + "make-dir": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", + "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", + "dev": true, + "requires": { + "pify": "3.0.0" + } + }, + "make-error": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, + "material-design-icons-iconfont": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-3.0.3.tgz", + "integrity": "sha1-FUoQhAR9Ticjf6f1o34Qdc7qbfI=" + }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", + "dev": true + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + }, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "0.1.7", + "readable-stream": "2.3.4" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mississippi": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.1.tgz", + "integrity": "sha512-/6rB8YXFbAtsUVRphIRQqB0+9c7VaPHCjVtvto+JqwVxgz8Zz+I+f68/JgQ+Pb4VlZb2svA9OtdXnHHsZz7ltg==", + "dev": true, + "requires": { + "concat-stream": "1.6.0", + "duplexify": "3.5.3", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.2", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "1.0.3", + "pumpify": "1.4.0", + "stream-each": "1.2.2", + "through2": "2.0.3" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "0.1.8", + "is-extendable": "0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "module-deps": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "browser-resolve": "1.11.2", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "defined": "1.0.0", + "detective": "4.7.1", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "parents": "1.0.1", + "readable-stream": "2.3.4", + "resolve": "1.5.0", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.3", + "xtend": "4.0.1" + }, + "dependencies": { + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "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.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + } + } + }, + "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 + }, + "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 + } + } + }, + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "1.3.1", + "thunky": "1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mydaterangepicker": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mydaterangepicker/-/mydaterangepicker-4.2.1.tgz", + "integrity": "sha1-8GPkdHAWJZua2IIVnvwcffCSIc0=" + }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.7.tgz", + "integrity": "sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "is-odd": "1.0.0", + "kind-of": "5.1.0", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "ncname": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", + "dev": true, + "requires": { + "xml-char-classes": "1.0.0" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "netmask": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", + "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", + "dev": true, + "optional": true + }, + "ng2-charts": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-1.6.0.tgz", + "integrity": "sha512-9w0WH69x5/nuqC1og2WaY39NbaBqTGIP1+5gZaH7/KPN6UEPonNg/pYnsIVklLj1DWPWXKa8+XXIJZ1jy5nLxg==", + "requires": { + "chart.js": "2.7.1" + } + }, + "ng2-cookies": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/ng2-cookies/-/ng2-cookies-1.0.12.tgz", + "integrity": "sha1-Pz5hPgE3sGSbcFxngHS0vQgUnMw=" + }, + "ngx-loading": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/ngx-loading/-/ngx-loading-1.0.14.tgz", + "integrity": "sha1-GXWMM+o/qbuW3KH0DKGdTYa4BCw=" + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "1.1.4" + } + }, + "node-forge": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", + "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", + "dev": true + }, + "node-gyp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", + "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", + "dev": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "osenv": "0.1.5", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.3.0", + "tar": "2.2.1", + "which": "1.3.0" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + } + } + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "1.4.1", + "browserify-zlib": "0.2.0", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "1.1.1", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.4", + "stream-browserify": "2.0.1", + "stream-http": "2.8.0", + "string_decoder": "1.0.3", + "timers-browserify": "2.0.6", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4" + } + }, + "node-modules-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/node-modules-path/-/node-modules-path-1.0.1.tgz", + "integrity": "sha1-QAlrCM560OoUaAhjr0ScfHWl0cg=", + "dev": true + }, + "node-sass": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.7.2.tgz", + "integrity": "sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "0.1.3", + "chalk": "1.1.3", + "cross-spawn": "3.0.1", + "gaze": "1.1.2", + "get-stdin": "4.0.1", + "glob": "7.1.2", + "in-publish": "2.0.0", + "lodash.assign": "4.2.0", + "lodash.clonedeep": "4.5.0", + "lodash.mergewith": "4.6.1", + "meow": "3.7.0", + "mkdirp": "0.5.1", + "nan": "2.8.0", + "node-gyp": "3.6.2", + "npmlog": "4.1.2", + "request": "2.79.0", + "sass-graph": "2.2.4", + "stdout-stream": "1.4.0", + "true-case-path": "1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true, + "optional": 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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "optional": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.14.1", + "is-my-json-valid": "2.17.2", + "pinkie-promise": "2.0.1" + } + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true, + "optional": true + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.4.3", + "uuid": "3.2.1" + } + }, + "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 + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true, + "optional": true + } + } + }, + "nodemailer": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", + "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=", + "dev": true, + "optional": true, + "requires": { + "libmime": "3.0.0", + "mailcomposer": "4.0.1", + "nodemailer-direct-transport": "3.3.2", + "nodemailer-shared": "1.1.0", + "nodemailer-smtp-pool": "2.8.2", + "nodemailer-smtp-transport": "2.7.2", + "socks": "1.1.9" + }, + "dependencies": { + "socks": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", + "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=", + "dev": true, + "optional": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "1.1.15" + } + } + } + }, + "nodemailer-direct-transport": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz", + "integrity": "sha1-6W+vuQNYVglH5WkBfZfmBzilCoY=", + "dev": true, + "optional": true, + "requires": { + "nodemailer-shared": "1.1.0", + "smtp-connection": "2.12.0" + } + }, + "nodemailer-fetch": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", + "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=", + "dev": true + }, + "nodemailer-shared": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", + "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", + "dev": true, + "requires": { + "nodemailer-fetch": "1.6.0" + } + }, + "nodemailer-smtp-pool": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz", + "integrity": "sha1-LrlNbPhXgLG0clzoU7nL1ejajHI=", + "dev": true, + "optional": true, + "requires": { + "nodemailer-shared": "1.1.0", + "nodemailer-wellknown": "0.1.10", + "smtp-connection": "2.12.0" + } + }, + "nodemailer-smtp-transport": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz", + "integrity": "sha1-A9ccdjFPFKx9vHvwM6am0W1n+3c=", + "dev": true, + "optional": true, + "requires": { + "nodemailer-shared": "1.1.0", + "nodemailer-wellknown": "0.1.10", + "smtp-connection": "2.12.0" + } + }, + "nodemailer-wellknown": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz", + "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=", + "dev": true + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "prepend-http": "1.0.4", + "query-string": "4.3.4", + "sort-keys": "1.1.2" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "2.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "nth-check": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", + "dev": true, + "requires": { + "boolbase": "1.0.0" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + } + } + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "obuf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", + "integrity": "sha1-EEEktsYCxnlogaBCVB0220OlJk4=", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "opn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", + "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==", + "dev": true, + "requires": { + "is-wsl": "1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.2" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "optional": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true, + "optional": true + } + } + }, + "options": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", + "dev": true + }, + "original": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", + "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", + "dev": true, + "requires": { + "url-parse": "1.0.5" + }, + "dependencies": { + "url-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", + "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", + "dev": true, + "requires": { + "querystringify": "0.0.4", + "requires-port": "1.0.0" + } + } + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "1.2.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pac-proxy-agent": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz", + "integrity": "sha512-QBELCWyLYPgE2Gj+4wUEiMscHrQ8nRPBzYItQNOHWavwBt25ohZHQC4qnd5IszdVVrFbLsQ+dPkm6eqdjJAmwQ==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1", + "get-uri": "2.0.1", + "http-proxy-agent": "1.0.0", + "https-proxy-agent": "1.0.0", + "pac-resolver": "2.0.0", + "raw-body": "2.3.2", + "socks-proxy-agent": "2.1.1" + } + }, + "pac-resolver": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-2.0.0.tgz", + "integrity": "sha1-mbiNLxk/ve78HJpSnB8yYKtSd80=", + "dev": true, + "optional": true, + "requires": { + "co": "3.0.6", + "degenerator": "1.0.4", + "ip": "1.0.1", + "netmask": "1.0.6", + "thunkify": "2.1.2" + }, + "dependencies": { + "co": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/co/-/co-3.0.6.tgz", + "integrity": "sha1-FEXyJsXrlWE45oyawwFn6n0ua9o=", + "dev": true, + "optional": true + }, + "ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.0.1.tgz", + "integrity": "sha1-x+NWzeoiWucbNtcPLnGpK6TkJZA=", + "dev": true, + "optional": true + } + } + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.4" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "2.3.2" + } + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "dev": true, + "requires": { + "asn1.js": "4.10.1", + "browserify-aes": "1.1.1", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.14" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "1.0.2" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "1.0.2" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true + }, + "path-proxy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-proxy/-/path-proxy-1.0.0.tgz", + "integrity": "sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4=", + "dev": true, + "optional": true, + "requires": { + "inflection": "1.3.8" + }, + "dependencies": { + "inflection": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.3.8.tgz", + "integrity": "sha1-y9Fg2p91sUw8xjV41POWeEvzAU4=", + "dev": true, + "optional": true + } + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", + "dev": true, + "requires": { + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.10" + } + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "2.1.0" + } + }, + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", + "dev": true, + "requires": { + "async": "1.5.2", + "debug": "2.6.9", + "mkdirp": "0.5.1" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "6.0.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.18.tgz", + "integrity": "sha512-X8MyLi3OYI1o71u0SsefWLpGBo5xnGiK1Pn+nrZFplc671Ts7L8aPwEbPIO8AWpulK5wuaVzyM9Rw6R8o7hYBw==", + "dev": true, + "requires": { + "chalk": "2.3.1", + "source-map": "0.6.1", + "supports-color": "5.2.0" + }, + "dependencies": { + "chalk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "5.2.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-message-helpers": "2.0.0", + "reduce-css-calc": "1.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "requires": { + "colormin": "1.1.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "uniqid": "4.1.1" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-import": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", + "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", + "dev": true, + "requires": { + "postcss": "6.0.18", + "postcss-value-parser": "3.3.0", + "read-cache": "1.0.0", + "resolve": "1.5.0" + } + }, + "postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", + "dev": true, + "requires": { + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1", + "postcss-load-options": "1.2.0", + "postcss-load-plugins": "2.3.0" + } + }, + "postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", + "dev": true, + "requires": { + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1" + } + }, + "postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "dev": true, + "requires": { + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1" + } + }, + "postcss-loader": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.0.tgz", + "integrity": "sha512-S/dKzpDwGFmP9g8eyCu9sUIV+/+3UooeTpYlsKf23qKDdrhHuA4pTSfytVu0rEJ0iDqUavXrgtOPq5KhNyNMOw==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "postcss": "6.0.18", + "postcss-load-config": "1.2.0", + "schema-utils": "0.4.5" + }, + "dependencies": { + "ajv": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", + "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", + "dev": true, + "requires": { + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "schema-utils": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "dev": true, + "requires": { + "ajv": "6.1.1", + "ajv-keywords": "3.1.0" + } + } + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-api": "1.6.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3", + "vendors": "1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000808", + "electron-to-chromium": "1.3.33" + } + }, + "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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "requires": { + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "requires": { + "alphanum-sort": "1.0.2", + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", + "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", + "dev": true, + "requires": { + "postcss": "6.0.18" + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.18" + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.18" + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.18" + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "requires": { + "is-absolute-url": "2.1.0", + "normalize-url": "1.9.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "requires": { + "flatten": "1.0.2", + "indexes-of": "1.0.1", + "uniq": "1.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "requires": { + "is-svg": "2.1.0", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "svgo": "0.7.2" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "requires": { + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-url": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.0.tgz", + "integrity": "sha512-VBP6uf6iL3AZra23nkPkOEkS/5azj1xf/toRrjfkolfFEgg9Gyzg9UhJZeIsz12EGKZTNVeGbPa2XtaZm/iZvg==", + "dev": true, + "requires": { + "mime": "1.6.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "postcss": "6.0.18", + "xxhashjs": "0.2.2" + } + }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "uniqs": "2.0.0" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "2.0.1", + "utila": "0.4.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "2.0.6" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "protractor": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz", + "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=", + "dev": true, + "requires": { + "@types/node": "6.0.101", + "@types/q": "0.0.32", + "@types/selenium-webdriver": "2.53.43", + "blocking-proxy": "0.0.5", + "chalk": "1.1.3", + "glob": "7.1.2", + "jasmine": "2.99.0", + "jasminewd2": "2.2.0", + "optimist": "0.6.1", + "q": "1.4.1", + "saucelabs": "1.3.0", + "selenium-webdriver": "3.0.1", + "source-map-support": "0.4.18", + "webdriver-js-extender": "1.0.0", + "webdriver-manager": "12.0.6" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "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 + }, + "webdriver-manager": { + "version": "12.0.6", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz", + "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", + "dev": true, + "requires": { + "adm-zip": "0.4.7", + "chalk": "1.1.3", + "del": "2.2.2", + "glob": "7.1.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "q": "1.4.1", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.5.0", + "xml2js": "0.4.19" + } + } + } + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "dev": true, + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "proxy-agent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-2.0.0.tgz", + "integrity": "sha1-V+tTR6qAXXTsaByyVknbo5yTNJk=", + "dev": true, + "optional": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1", + "http-proxy-agent": "1.0.0", + "https-proxy-agent": "1.0.0", + "lru-cache": "2.6.5", + "pac-proxy-agent": "1.1.0", + "socks-proxy-agent": "2.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz", + "integrity": "sha1-5W1jVBSO3o13B7WNFDIg/QjfD9U=", + "dev": true, + "optional": true + } + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "parse-asn1": "5.1.0", + "randombytes": "2.0.6" + } + }, + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", + "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", + "dev": true, + "requires": { + "duplexify": "3.5.3", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qjobs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", + "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "randomfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz", + "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==", + "dev": true, + "requires": { + "randombytes": "2.0.6", + "safe-buffer": "5.1.1" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "2.3.4" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + } + } + }, + "readable-stream": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", + "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.4", + "set-immediate-shim": "1.0.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } + }, + "redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "dev": true, + "optional": true, + "requires": { + "double-ended-queue": "2.1.0-0", + "redis-commands": "1.3.1", + "redis-parser": "2.6.0" + } + }, + "redis-commands": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz", + "integrity": "sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs=", + "dev": true, + "optional": true + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", + "dev": true, + "optional": true + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "math-expression-evaluator": "1.2.17", + "reduce-function-call": "1.0.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", + "dev": true, + "requires": { + "balanced-match": "0.4.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reflect-metadata": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", + "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==", + "dev": true + }, + "regenerate": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", + "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "regex-not": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", + "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "1.3.3", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "0.5.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", + "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", + "dev": true, + "requires": { + "css-select": "1.2.0", + "dom-converter": "0.1.4", + "htmlparser2": "3.3.0", + "strip-ansi": "3.0.1", + "utila": "0.3.3" + }, + "dependencies": { + "utila": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", + "dev": true + } + } + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "requestretry": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", + "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", + "dev": true, + "optional": true, + "requires": { + "extend": "3.0.1", + "lodash": "4.17.5", + "request": "2.81.0", + "when": "3.7.8" + }, + "dependencies": { + "when": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", + "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", + "dev": true, + "optional": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "dev": true, + "requires": { + "hash-base": "2.0.2", + "inherits": "2.0.3" + } + }, + "roboto-fontface": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.8.0.tgz", + "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "1.2.0" + } + }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, + "rxjs": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", + "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", + "requires": { + "symbol-observable": "1.0.1" + } + }, + "rxjs-websockets": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rxjs-websockets/-/rxjs-websockets-4.0.0.tgz", + "integrity": "sha512-b9mdl9S81FBc33R6Ywf1ePMJ+jNKt9ebrDS5+h2TH80Gp8okj5Fbydo36dQ4Julq1Re0DNmXhXEMYlEiJYJz3g==" + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.5", + "scss-tokenizer": "0.2.3", + "yargs": "7.1.0" + } + }, + "sass-loader": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz", + "integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==", + "dev": true, + "requires": { + "async": "2.6.0", + "clone-deep": "0.3.0", + "loader-utils": "1.1.0", + "lodash.tail": "4.1.1", + "pify": "3.0.0" + } + }, + "saucelabs": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.3.0.tgz", + "integrity": "sha1-0kDoAJ33+ocwbsRXimm6O1xCT+4=", + "dev": true, + "requires": { + "https-proxy-agent": "1.0.0" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "requires": { + "ajv": "5.5.2" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "optional": true, + "requires": { + "js-base64": "2.4.3", + "source-map": "0.4.4" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "optional": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", + "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", + "dev": true, + "requires": { + "adm-zip": "0.4.7", + "rimraf": "2.6.2", + "tmp": "0.0.30", + "xml2js": "0.4.19" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + } + } + }, + "selfsigned": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.2.tgz", + "integrity": "sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=", + "dev": true, + "requires": { + "node-forge": "0.7.1" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "5.5.0" + } + }, + "send": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", + "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "1.0.3", + "http-errors": "1.6.2", + "mime-types": "2.1.17", + "parseurl": "1.3.2" + } + }, + "serve-static": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "dev": true, + "requires": { + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dev": true, + "requires": { + "to-object-path": "0.3.0" + } + }, + "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", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", + "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "dev": true, + "requires": { + "is-extendable": "0.1.1", + "kind-of": "2.0.1", + "lazy-cache": "0.2.7", + "mixin-object": "2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "requires": { + "json-stable-stringify": "0.0.1", + "sha.js": "2.4.10" + }, + "dependencies": { + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "silent-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/silent-error/-/silent-error-1.1.0.tgz", + "integrity": "sha1-IglwbxyFCp8dENDYQJGLRvJuG8k=", + "dev": true, + "requires": { + "debug": "2.6.9" + } + }, + "slack-node": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/slack-node/-/slack-node-0.2.0.tgz", + "integrity": "sha1-3kuN3aqLeT9h29KTgQT9q/N9+jA=", + "dev": true, + "optional": true, + "requires": { + "requestretry": "1.13.0" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "smart-buffer": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", + "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", + "dev": true + }, + "smtp-connection": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz", + "integrity": "sha1-1275EnyyPCJZ7bHoNJwujV4tdME=", + "dev": true, + "requires": { + "httpntlm": "1.6.1", + "nodemailer-shared": "1.1.0" + } + }, + "snapdragon": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", + "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "2.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "socket.io": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", + "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "engine.io": "3.1.4", + "socket.io-adapter": "1.1.1", + "socket.io-client": "2.0.4", + "socket.io-parser": "3.1.2" + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true + }, + "socket.io-client": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", + "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "2.6.9", + "engine.io-client": "3.1.4", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "3.1.2", + "to-array": "0.1.4" + } + }, + "socket.io-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", + "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "2.6.9", + "has-binary2": "1.0.2", + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "0.10.0", + "uuid": "3.2.1" + } + }, + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "eventsource": "0.1.6", + "faye-websocket": "0.11.1", + "inherits": "2.0.3", + "json3": "3.3.2", + "url-parse": "1.2.0" + }, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + } + } + }, + "socks": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", + "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", + "dev": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "1.1.15" + } + }, + "socks-proxy-agent": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz", + "integrity": "sha512-sFtmYqdUK5dAMh85H0LEVFUCO7OhJJe1/z2x/Z6mxp3s7/QPf1RkZmpZy+BpuU0bEjcV9npqKjq9Y3kwFUjnxw==", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "extend": "3.0.1", + "socks": "1.1.10" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "1.1.0" + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "dev": true, + "requires": { + "atob": "2.0.3", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "spdy": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "dev": true, + "requires": { + "debug": "2.6.9", + "handle-thing": "1.2.5", + "http-deceiver": "1.2.7", + "safe-buffer": "5.1.1", + "select-hose": "2.0.0", + "spdy-transport": "2.0.20" + } + }, + "spdy-transport": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", + "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", + "dev": true, + "requires": { + "debug": "2.6.9", + "detect-node": "2.0.3", + "hpack.js": "2.1.6", + "obuf": "1.1.1", + "readable-stream": "2.3.4", + "safe-buffer": "5.1.1", + "wbuf": "1.7.2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "ssri": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.2.2.tgz", + "integrity": "sha512-hm46mN8YSzjGuJtVocXPjwo0yTRXobXqYuK/tV6gr557/tRck4yWXKPRW8OxyJgRvcL3QgX+5ng/kMHBMco7KA==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stdout-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", + "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "2.3.4" + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "0.1.4", + "readable-stream": "2.3.4" + } + }, + "stream-each": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + } + }, + "stream-http": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", + "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", + "dev": true, + "requires": { + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.4", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4" + } + }, + "streamroller": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "dev": true, + "requires": { + "date-format": "1.2.0", + "debug": "3.1.0", + "mkdirp": "0.5.1", + "readable-stream": "2.3.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "style-loader": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.13.2.tgz", + "integrity": "sha1-dFMzhM9pjHEEx5URULSXF63C87s=", + "dev": true, + "requires": { + "loader-utils": "1.1.0" + } + }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.0", + "debug": "2.6.9", + "glob": "7.0.6", + "mkdirp": "0.5.1", + "sax": "0.5.8", + "source-map": "0.1.43" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "stylus-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.1.tgz", + "integrity": "sha1-d/SzT9Aw0lsmF7z1UT21sHMMQIk=", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "lodash.clonedeep": "4.5.0", + "when": "3.6.4" + } + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "requires": { + "coa": "1.0.4", + "colors": "1.1.2", + "csso": "2.3.2", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", + "whet.extend": "0.9.9" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" + }, + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "requires": { + "acorn-node": "1.3.0" + } + }, + "tapable": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", + "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "optional": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.4", + "xtend": "4.0.1" + } + }, + "thunkify": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", + "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", + "dev": true, + "optional": true + }, + "thunky": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", + "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", + "dev": true + }, + "time-stamp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", + "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", + "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", + "dev": true, + "requires": { + "setimmediate": "1.0.5" + } + }, + "timespan": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", + "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=", + "dev": true, + "optional": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "to-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", + "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "regex-not": "1.0.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + } + } + }, + "toposort": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.6.tgz", + "integrity": "sha1-wxdI5V0hDv/AD9zcfW5o19e7nOw=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tree-kill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", + "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", + "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", + "dev": true, + "optional": true, + "requires": { + "glob": "6.0.4" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "optional": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "ts-node": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-4.1.0.tgz", + "integrity": "sha512-xcZH12oVg9PShKhy3UHyDmuDLV3y7iKwX25aMVPt1SIXSuAfWkFiGPEkg+th8R4YKW/QCxDoW7lJdb15lx6QWg==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.3.1", + "diff": "3.4.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.3", + "tsconfig": "7.0.0", + "v8flags": "3.0.1", + "yn": "2.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "5.2.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", + "dev": true, + "requires": { + "source-map": "0.6.1" + } + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "tsickle": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.26.0.tgz", + "integrity": "sha512-eWJ2CUfttGK0LqF9iJ/Avnxbj4M+fCyJ50Zag3wm73Fut1hsasPRHKxKdrMWVj4BMHnQNx7TO+DdNmLmJTSuNw==", + "dev": true, + "requires": { + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map": "0.5.7", + "source-map-support": "0.4.18" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==" + }, + "tslint": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", + "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.3.1", + "commander": "2.14.1", + "diff": "3.4.0", + "glob": "7.1.2", + "js-yaml": "3.7.0", + "minimatch": "3.0.4", + "resolve": "1.5.0", + "semver": "5.5.0", + "tslib": "1.9.0", + "tsutils": "2.21.1" + }, + "dependencies": { + "chalk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "5.2.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "tsscmp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", + "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=", + "dev": true, + "optional": true + }, + "tsutils": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.21.1.tgz", + "integrity": "sha512-heMkdeQ9iUc90ynfiNo5Y+GXrEEGy86KMvnSTfHO+Q40AuNQ1lZGXcv58fuU9XTUxI0V7YIN9xPN+CO9b1Gn3w==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz", + "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==", + "dev": true + }, + "uglify-js": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.11.tgz", + "integrity": "sha512-AKLsYcdV+sS5eAE4NtVXF6f2u/DCQynQm0jTGxF261+Vltu1dYNuHzjqDmk11gInj+H/zJIM2EAwXG3MzPb3VA==", + "dev": true, + "requires": { + "commander": "2.14.1", + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "uglifyjs-webpack-plugin": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.8.tgz", + "integrity": "sha512-XG8/QmR1pyPeE1kj2aigo5kos8umefB31zW+PMvAAytHSB0T/vQvN6sqt8+Sh+y0b0A7zlmxNi2dzRnj0wcqGA==", + "dev": true, + "requires": { + "cacache": "10.0.2", + "find-cache-dir": "1.0.0", + "schema-utils": "0.4.5", + "serialize-javascript": "1.4.0", + "source-map": "0.6.1", + "uglify-es": "3.3.9", + "webpack-sources": "1.1.0", + "worker-farm": "1.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", + "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", + "dev": true, + "requires": { + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "schema-utils": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "dev": true, + "requires": { + "ajv": "6.1.1", + "ajv-keywords": "3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "2.13.0", + "source-map": "0.6.1" + } + } + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "umd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", + "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=", + "dev": true + }, + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqid": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", + "dev": true, + "requires": { + "macaddress": "0.2.8" + } + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "0.1.4" + } + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "upath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.2.tgz", + "integrity": "sha512-fCmij7T5LnwUme3dbnVSejvOHHlARjB3ikJFwgZfz386pHmf/gueuTLRFU94FZEaeCLlbQrweiUU700gG41tUw==", + "dev": true, + "requires": { + "lodash.endswith": "4.2.1", + "lodash.isfunction": "3.0.9", + "lodash.isstring": "4.0.1", + "lodash.startswith": "4.2.1" + } + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", + "integrity": "sha512-h3qf9TNn53BpuXTTcpC+UehiRrl0Cv45Yr/xWayApjw6G8Bg2dGke7rIwDQ39piciWCWrC+WiqLjOh3SUp9n0Q==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "mime": "1.6.0", + "schema-utils": "0.3.0" + } + }, + "url-parse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", + "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", + "dev": true, + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + }, + "dependencies": { + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", + "dev": true + } + } + }, + "use": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", + "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "isobject": "3.0.1", + "lazy-cache": "2.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "dev": true, + "requires": { + "set-getter": "0.1.0" + } + } + } + }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "tmp": "0.0.33" + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", + "dev": true + }, + "uws": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", + "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=", + "dev": true, + "optional": true + }, + "v8flags": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", + "integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", + "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", + "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", + "dev": true, + "requires": { + "async": "2.6.0", + "chokidar": "1.7.0", + "graceful-fs": "4.1.11" + } + }, + "wbuf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", + "integrity": "sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=", + "dev": true, + "requires": { + "minimalistic-assert": "1.0.0" + } + }, + "web-animations-js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz", + "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA=" + }, + "webdriver-js-extender": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", + "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", + "dev": true, + "requires": { + "@types/selenium-webdriver": "2.53.43", + "selenium-webdriver": "2.53.3" + }, + "dependencies": { + "adm-zip": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", + "integrity": "sha1-ph7VrmkFw66lizplfSUDMJEFJzY=", + "dev": true + }, + "sax": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", + "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=", + "dev": true + }, + "selenium-webdriver": { + "version": "2.53.3", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", + "integrity": "sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU=", + "dev": true, + "requires": { + "adm-zip": "0.4.4", + "rimraf": "2.6.2", + "tmp": "0.0.24", + "ws": "1.1.5", + "xml2js": "0.4.4" + } + }, + "tmp": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", + "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=", + "dev": true + }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "dev": true + }, + "ws": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", + "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", + "dev": true, + "requires": { + "options": "0.0.6", + "ultron": "1.0.2" + } + }, + "xml2js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", + "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", + "dev": true, + "requires": { + "sax": "0.6.1", + "xmlbuilder": "9.0.7" + } + } + } + }, + "webpack": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz", + "integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==", + "dev": true, + "requires": { + "acorn": "5.4.1", + "acorn-dynamic-import": "2.0.2", + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "async": "2.6.0", + "enhanced-resolve": "3.4.1", + "escope": "3.6.0", + "interpret": "1.1.0", + "json-loader": "0.5.7", + "json5": "0.5.1", + "loader-runner": "2.3.0", + "loader-utils": "1.1.0", + "memory-fs": "0.4.1", + "mkdirp": "0.5.1", + "node-libs-browser": "2.1.0", + "source-map": "0.5.7", + "supports-color": "4.5.0", + "tapable": "0.2.8", + "uglifyjs-webpack-plugin": "0.4.6", + "watchpack": "1.4.0", + "webpack-sources": "1.1.0", + "yargs": "8.0.2" + }, + "dependencies": { + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "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": { + "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 + }, + "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" + } + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "requires": { + "source-map": "0.5.7", + "uglify-js": "2.8.29", + "webpack-sources": "1.1.0" + } + }, + "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 + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "dev": true, + "requires": { + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + } + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + } + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "0.1.8", + "source-map": "0.4.4" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "dev": true, + "requires": { + "memory-fs": "0.4.1", + "mime": "1.6.0", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "time-stamp": "2.0.0" + } + }, + "webpack-dev-server": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz", + "integrity": "sha512-ombhu5KsO/85sVshIDTyQ5HF3xjZR3N0sf5Ao6h3vFwpNyzInEzA1GV3QPVjTMLTNckp8PjfG1PFGznzBwS5lg==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "array-includes": "3.0.3", + "bonjour": "3.5.0", + "chokidar": "2.0.2", + "compression": "1.7.1", + "connect-history-api-fallback": "1.5.0", + "debug": "3.1.0", + "del": "3.0.0", + "express": "4.16.2", + "html-entities": "1.2.1", + "http-proxy-middleware": "0.17.4", + "import-local": "1.0.0", + "internal-ip": "1.2.0", + "ip": "1.1.5", + "killable": "1.0.0", + "loglevel": "1.6.1", + "opn": "5.1.0", + "portfinder": "1.0.13", + "selfsigned": "1.10.2", + "serve-index": "1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.1.4", + "spdy": "3.4.7", + "strip-ansi": "3.0.1", + "supports-color": "5.2.0", + "webpack-dev-middleware": "1.12.2", + "yargs": "6.6.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "3.1.5", + "normalize-path": "2.1.1" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "braces": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", + "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.1" + } + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "chokidar": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.2.tgz", + "integrity": "sha512-l32Hw3wqB0L2kGVmSbK/a+xXLDrUEsc84pSgMkmwygHvD7ubRsP/vxxHa5BtB6oix1XLLVCHyYMsckRXxThmZw==", + "dev": true, + "requires": { + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.0", + "fsevents": "1.1.3", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.0.2" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.5.tgz", + "integrity": "sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.0", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.7", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "4.2.1" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "requires": { + "camelcase": "3.0.0" + } + } + } + }, + "webpack-merge": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.1.tgz", + "integrity": "sha512-geQsZ86YkXOVOjvPC5yv3JSNnL6/X3Kzh935AQ/gJNEYXEfJDQFu/sdFuktS9OW2JcH/SJec8TGfRdrpHshH7A==", + "dev": true, + "requires": { + "lodash": "4.17.5" + } + }, + "webpack-sources": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", + "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "dev": true, + "requires": { + "source-list-map": "2.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.0.4.tgz", + "integrity": "sha1-j6yKfo61n8ahZ2ioXJ2U7n+dDts=", + "dev": true, + "requires": { + "webpack-core": "0.6.9" + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": "0.4.10", + "websocket-extensions": "0.1.3" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "requires": { + "string-width": "1.0.2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "worker-farm": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.2.tgz", + "integrity": "sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==", + "dev": true, + "requires": { + "errno": "0.1.7", + "xtend": "4.0.1" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + }, + "xml-char-classes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", + "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=", + "dev": true + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": "1.2.4", + "xmlbuilder": "9.0.7" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "dev": true, + "optional": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "dev": true, + "requires": { + "cuint": "0.2.2" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zone.js": { + "version": "0.8.20", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.20.tgz", + "integrity": "sha512-FXlA37ErSXCMy5RNBcGFgCI/Zivqzr0D19GuvDxhcYIJc7xkFp6c29DKyODJu0Zo+EMyur/WPPgcBh1EHjB9jA==" + } + } +} diff --git a/ui/package.json b/ui/package.json index 130150fdd3d..7de1c82cf3c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,82 +1,82 @@ -{ - "name": "openems-ui", - "version": "2018.2.0", - "license": "AGPL", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "build-edge": "ng build -prod --env=edge --base-href / --output-path=target/edge", - "build-backend": "ng build -prod --env=backend --base-href /m/ --output-path=target/backend", - "test": "ng test", - "lint": "ng lint", - "e2e": "ng e2e" - }, - "private": true, - "dependencies": { - "@angular/animations": "^5.2.0", - "@angular/cdk": "^5.2.0", - "@angular/common": "^5.2.0", - "@angular/compiler": "^5.2.0", - "@angular/core": "^5.2.0", - "@angular/flex-layout": "2.0.0-beta.12", - "@angular/forms": "^5.2.0", - "@angular/http": "^5.2.0", - "@angular/material": "^5.2.0", - "@angular/platform-browser": "^5.2.0", - "@angular/platform-browser-dynamic": "^5.2.0", - "@angular/platform-server": "^5.2.0", - "@angular/router": "^5.2.0", - "@ngx-translate/core": "^9.1.1", - "@types/d3": "4.12.0", - "angular2-toaster": "5.0.0-alpha.1", - "angular2-uuid": "^1.1.1", - "chart.js": "2.7.1", - "classlist.js": "^1.1.20150312", - "core-js": "^2.4.1", - "d3": "4.13.0", - "d3-array": "1.2.1", - "d3-brush": "1.0.4", - "d3-color": "1.0.3", - "d3-force": "1.1.0", - "d3-format": "1.2.2", - "d3-hierarchy": "1.1.5", - "d3-interpolate": "1.1.6", - "d3-scale": "2.0.0", - "d3-selection": "1.3.0", - "d3-shape": "1.2.0", - "date-fns": "^2.0.0-alpha.7", - "hammerjs": "2.0.8", - "intl": "^1.2.5", - "material-design-icons-iconfont": "3.0.3", - "mydaterangepicker": "^4.2.1", - "ng2-charts": "^1.6.0", - "ng2-cookies": "^1.0.12", - "ngx-loading": "^1.0.14", - "roboto-fontface": "0.8.0", - "rxjs": "^5.5.6", - "rxjs-websockets": "4.0.0", - "web-animations-js": "^2.3.1", - "zone.js": "^0.8.19" - }, - "devDependencies": { - "@angular/cli": "1.6.7", - "@angular/compiler-cli": "^5.2.0", - "@angular/language-service": "^5.2.0", - "@types/jasmine": "~2.8.3", - "@types/jasminewd2": "~2.0.2", - "@types/node": "~6.0.60", - "codelyzer": "^4.0.1", - "jasmine-core": "~2.8.0", - "jasmine-spec-reporter": "~4.2.1", - "karma": "~2.0.0", - "karma-chrome-launcher": "~2.2.0", - "karma-coverage-istanbul-reporter": "^1.2.1", - "karma-jasmine": "~1.1.0", - "karma-jasmine-html-reporter": "^0.2.2", - "protractor": "~5.1.2", - "ts-node": "~4.1.0", - "tslint": "~5.9.1", - "typescript": "~2.5.3" - } -} +{ + "name": "openems-ui", + "version": "2018.3.0", + "license": "AGPL", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "build-edge": "ng build -prod --env=edge --base-href / --output-path=target/edge", + "build-backend": "ng build -prod --env=backend --base-href /m/ --output-path=target/backend", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "^5.2.0", + "@angular/cdk": "^5.2.0", + "@angular/common": "^5.2.0", + "@angular/compiler": "^5.2.0", + "@angular/core": "^5.2.0", + "@angular/flex-layout": "2.0.0-beta.12", + "@angular/forms": "^5.2.0", + "@angular/http": "^5.2.0", + "@angular/material": "^5.2.0", + "@angular/platform-browser": "^5.2.0", + "@angular/platform-browser-dynamic": "^5.2.0", + "@angular/platform-server": "^5.2.0", + "@angular/router": "^5.2.0", + "@ngx-translate/core": "^9.1.1", + "@types/d3": "4.12.0", + "angular2-toaster": "5.0.0-alpha.1", + "angular2-uuid": "^1.1.1", + "chart.js": "2.7.1", + "classlist.js": "^1.1.20150312", + "core-js": "^2.4.1", + "d3": "4.13.0", + "d3-array": "1.2.1", + "d3-brush": "1.0.4", + "d3-color": "1.0.3", + "d3-force": "1.1.0", + "d3-format": "1.2.2", + "d3-hierarchy": "1.1.5", + "d3-interpolate": "1.1.6", + "d3-scale": "2.0.0", + "d3-selection": "1.3.0", + "d3-shape": "1.2.0", + "date-fns": "^2.0.0-alpha.7", + "hammerjs": "2.0.8", + "intl": "^1.2.5", + "material-design-icons-iconfont": "3.0.3", + "mydaterangepicker": "^4.2.1", + "ng2-charts": "^1.6.0", + "ng2-cookies": "^1.0.12", + "ngx-loading": "^1.0.14", + "roboto-fontface": "0.8.0", + "rxjs": "^5.5.6", + "rxjs-websockets": "4.0.0", + "web-animations-js": "^2.3.1", + "zone.js": "^0.8.19" + }, + "devDependencies": { + "@angular/cli": "1.6.7", + "@angular/compiler-cli": "^5.2.0", + "@angular/language-service": "^5.2.0", + "@types/jasmine": "~2.8.3", + "@types/jasminewd2": "~2.0.2", + "@types/node": "~6.0.60", + "codelyzer": "^4.0.1", + "jasmine-core": "~2.8.0", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~2.0.0", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "^1.2.1", + "karma-jasmine": "~1.1.0", + "karma-jasmine-html-reporter": "^0.2.2", + "protractor": "~5.1.2", + "ts-node": "~4.1.0", + "tslint": "~5.9.1", + "typescript": "~2.5.3" + } +} diff --git a/ui/pom.xml b/ui/pom.xml index 4cd39ddaa1e..9173b80d6ac 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -7,7 +7,7 @@ io.openems pom - 2018.2.0 + 2018.3.0 edge pom diff --git a/ui/src/app/about/about.component.html b/ui/src/app/about/about.component.html index 11f7204d3f9..29bec8bce65 100644 --- a/ui/src/app/about/about.component.html +++ b/ui/src/app/about/about.component.html @@ -17,12 +17,12 @@ About.Sourcecode
      1. - - About.Build: 2018.2.0 (2018-03-01) + + About.Build: 2018.3.0 (2018-03-14)
      2. From b83a426d52820ab1aa5db390021771f6a3b7678e Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 23:46:35 +0100 Subject: [PATCH 153/156] Fix log output of EdgeWebsocketServer --- .../impl/provider/EdgeWebsocketServer.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java index 6c0312a6796..402b5448ccb 100644 --- a/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java +++ b/io.openems.backend.edgewebsocket.impl.provider/src/io/openems/backend/edgewebsocket/impl/provider/EdgeWebsocketServer.java @@ -60,7 +60,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { if (this.websocketsMap.containsKey(edgeId)) { WebSocket oldWebsocket = this.websocketsMap.get(edgeId); oldWebsocket.closeConnection(CloseFrame.REFUSE, - "Another device with this apikey [" + apikey + "] connected."); + "Another Edge with this apikey [" + apikey + "] connected."); } // add websocket to local cache this.websocketsMap.put(edgeId, websocket); @@ -74,7 +74,7 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { JsonObject jReply = DefaultMessages.openemsConnectionSuccessfulReply(); WebSocketUtils.send(websocket, jReply); - // announce device as online + // announce Edge as online for (int edgeId : edgeIds) { Map properties = new HashMap<>(); properties.put(BackendEventConstants.PROPERTY_KEY_EDGE_ID, edgeId); @@ -86,9 +86,9 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { for (int edgeId : edgeIds) { Optional edgeOpt = this.parent.metadataService.getEdgeOpt(edgeId); if (edgeOpt.isPresent()) { - log.info("Device [" + edgeOpt.get().getName() + "] connected."); + log.info("Edge [" + edgeOpt.get().getName() + "] connected."); } else { - log.info("Device [ID:" + edgeId + "] connected."); + log.info("Edge [ID:" + edgeId + "] connected."); } } } catch (OpenemsException e) { @@ -97,12 +97,12 @@ protected void _onOpen(WebSocket websocket, ClientHandshake handshake) { WebSocketUtils.sendOrLogError(websocket, jReply); // close websocket websocket.closeConnection(CloseFrame.REFUSE, - "OpenEMS connection failed. Apikey [" + apikey + "]. Error: " + e.getMessage()); + "Connection to backend failed. Apikey [" + apikey + "]. Error: " + e.getMessage()); } } /** - * Message event of websocket. Handles a new message. At this point the device + * Message event of websocket. Handles a new message. At this point the Edge * is already authenticated. */ @Override @@ -189,7 +189,7 @@ protected void _onClose(WebSocket websocket) { } } - // announce device as offline + // announce Edge as offline for (int edgeId : edgeIds) { Map properties = new HashMap<>(); properties.put(BackendEventConstants.PROPERTY_KEY_EDGE_ID, edgeId); From 51a78f41d69cadedbe791c6119fc226330dc2f15 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 23:46:51 +0100 Subject: [PATCH 154/156] Reduce OSGi logs to INFO --- .../src/io/openems/common/config/JsonPersistenceManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java index 701bd66e65f..95947103648 100644 --- a/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java +++ b/io.openems.common/src/io/openems/common/config/JsonPersistenceManager.java @@ -149,6 +149,7 @@ private void loadDefaultConfig() { log4j.put("log4j.logger.org.eclipse.osgi", "WARN"); log4j.put("log4j.logger.org.apache.felix.configadmin", "INFO"); log4j.put("log4j.logger.sun.net.www.protocol.http.HttpURLConnection", "INFO"); + log4j.put("log4j.logger.io.openems", "INFO"); this.configs.put(log4j.getPid(), log4j); } log.info("Finished Load default config"); From 0320f69d7e0a0a69f8de50dca28fc6be35a0c67b Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 23:47:23 +0100 Subject: [PATCH 155/156] Fix energytable for FENECON Mini --- .../energytable/energytable.component.html | 10 +- .../overview/state/state.component.html | 52 +- ui/src/app/shared/device/config.ts | 554 +++++++-------- ui/src/app/shared/service/websocket.ts | 648 +++++++++--------- 4 files changed, 628 insertions(+), 636 deletions(-) diff --git a/ui/src/app/device/overview/energytable/energytable.component.html b/ui/src/app/device/overview/energytable/energytable.component.html index 4b106aed8d4..220fcaf089f 100644 --- a/ui/src/app/device/overview/energytable/energytable.component.html +++ b/ui/src/app/device/overview/energytable/energytable.component.html @@ -39,20 +39,20 @@ - + General.ChargePower L1 {{ sum.chargeActivePowerACL1 }} W - + L2 {{ sum.chargeActivePowerACL2 }} W - + L3 {{ sum.chargeActivePowerACL3 }} @@ -64,13 +64,13 @@ {{ sum.dischargeActivePowerACL1 }} W - + L2 {{ sum.dischargeActivePowerACL2 }} W - + L3 {{ sum.dischargeActivePowerACL3 }} diff --git a/ui/src/app/device/overview/state/state.component.html b/ui/src/app/device/overview/state/state.component.html index 3869f529009..7dcedbab173 100644 --- a/ui/src/app/device/overview/state/state.component.html +++ b/ui/src/app/device/overview/state/state.component.html @@ -9,37 +9,27 @@ - - - - + + + + {{ warningsAndFaults.thing.name }} ({{ warningsAndFaults.thing.id }}) + + + {{ warningsAndFaults.thing.id }} + + + + + + + + + + + + + +
        - {{ warningsAndFaults.thing.name }} -
        - ({{ warningsAndFaults.thing.id }}) -
        - {{ warningsAndFaults.thing.id }} -
        - - - - - - -
        - Fehler: - {{ line.name }} -
        - - - - - - -
        - Warnung: - {{ line.name }} -
        -
        Fehler{{ line.name }}
        Warnung{{ line.name }}
        diff --git a/ui/src/app/shared/device/config.ts b/ui/src/app/shared/device/config.ts index 02a984ca62c..0197a22f603 100644 --- a/ui/src/app/shared/device/config.ts +++ b/ui/src/app/shared/device/config.ts @@ -1,277 +1,279 @@ -import { DefaultTypes } from '../service/defaulttypes' -import { Role } from '../type/role' -import { Widget } from '../type/widget' - -export class ConfigImpl implements DefaultTypes.Config { - - // Attributes from Config interface - public readonly things: { - [id: string]: { - id: string, - alias: string, - class: string | string[], - [channel: string]: any - } - }; - public readonly meta: { - [clazz: string]: { - implements: string[], - channels: { - [channel: string]: { - name: string, - title: string, - type: string | string[], - optional: boolean, - array: boolean, - readRoles: Role[], - writeRoles: Role[], - defaultValue: string - } - } - } - }; - - // A list of thing ids which are matching Natures. (e.g. ["ess0", "ess1"]) - public readonly storageThings: string[] = []; - public readonly chargers: string[] = []; - public readonly gridMeters: string[] = []; - public readonly productionMeters: string[] = []; - public readonly consumptionMeters: string[] = []; - public readonly otherMeters: string[] = []; // TODO show otherMeters in Energymonitor - public readonly bridges: string[] = []; - public readonly scheduler: string = null; - public readonly controllers: string[] = []; - public readonly persistences: string[] = []; - public readonly simulatorDevices: string[] = []; - public readonly evcsDevices: string[] = []; - - constructor(private readonly config: DefaultTypes.Config) { - // convert role-strings to Role-objects - for (let clazz in config.meta) { - for (let channel in config.meta[clazz].channels) { - let roles: Role[] = []; - for (let roleString of config.meta[clazz].channels[channel].readRoles) { - roles.push(Role.getRole("" + roleString /* convert to string */)); - } - config.meta[clazz].channels[channel].readRoles = roles; - } - } - - Object.assign(this, config); - - let storageThings: string[] = [] - let chargers: string[] = []; - let gridMeters: string[] = []; - let productionMeters: string[] = []; - let consumptionMeters: string[] = []; - let otherMeters: string[] = []; - let bridges: string[] = []; - let scheduler: string = null; - let controllers: string[] = []; - let persistences: string[] = []; - let simulatorDevices: string[] = []; - let evcsDevices: string[] = []; - - for (let thingId in config.things) { - let thing = config.things[thingId]; - let i = this.getImplements(thing); - - /* - * Natures - */ - // Ess - if (i.includes("EssNature") - && !i.includes("EssClusterNature") /* ignore cluster */ - && !i.includes("AsymmetricSymmetricCombinationEssNature") /* ignore symmetric Ess of Pro 9-12 */) { - storageThings.push(thingId); - } - // Meter - if (i.includes("MeterNature")) { - if ("type" in thing) { - if (thing.type == 'grid') { - gridMeters.push(thingId); - } else if (thing.type === "production") { - productionMeters.push(thingId); - } else if (thing.type === "consumption") { - consumptionMeters.push(thingId); - } else { - otherMeters.push(thingId); - } - } - } - // Charger - if (i.includes("ChargerNature")) { - productionMeters.push(thingId); - chargers.push(thingId); - } - /* - * Other Things - */ - // Bridge - if (i.includes("io.openems.api.bridge.Bridge")) { - bridges.push(thingId); - } - // Scheduler - if (i.includes("io.openems.api.scheduler.Scheduler")) { - scheduler = thingId; - } - // Controller - if (i.includes("io.openems.api.controller.Controller")) { - controllers.push(thingId); - } - // Persistence - if (i.includes("io.openems.api.persistence.Persistence")) { - persistences.push(thingId); - } - // Simulator Devices - if (i.includes("io.openems.impl.device.simulator.Simulator")) { - simulatorDevices.push(thingId); - } - // Simulator Devices - if (i.includes("KebaDeviceNature")) { - evcsDevices.push(thingId); - } - } - - this.storageThings = storageThings.sort(); - this.chargers = chargers.sort(); - this.gridMeters = gridMeters.sort(); - this.productionMeters = productionMeters.sort(); - this.bridges = bridges.sort(); - this.scheduler = scheduler; - this.controllers = controllers; - this.persistences = persistences; - this.simulatorDevices = simulatorDevices; - this.evcsDevices = evcsDevices; - } - - public getStateChannels(): DefaultTypes.ChannelAddresses { - let result: DefaultTypes.ChannelAddresses = {} - - // Set "ignoreNatures" - for (let thingId in this.config.things) { - result[thingId] = ["State"]; - } - return result; - } - - - /** - * Return ChannelAddresses of power channels - */ - public getPowerChannels(): DefaultTypes.ChannelAddresses { - let ignoreNatures = { EssClusterNature: true }; - let result: DefaultTypes.ChannelAddresses = {} - - // Set "ignoreNatures" - for (let thingId of this.storageThings) { - let i = this.getImplements(this.config.things[thingId]); - - if (i.includes("FeneconCommercialEss")) { // workaround to ignore asymmetric meter for commercial - ignoreNatures["AsymmetricMeterNature"] = true; - } - } - // Parse all things - for (let thingId in this.config.things) { - let clazz = this.config.things[thingId].class; // TODO casting - let i = this.getImplements(this.config.things[thingId]); - let channels = []; - // ESS - if (i.includes("EssNature") - && !i.includes("EssClusterNature") /* ignore cluster */ - && !i.includes("AsymmetricSymmetricCombinationEssNature") /* ignore symmetric Ess of Pro 9-12 */) { - if (i.includes("AsymmetricEssNature")) { - channels.push("ActivePowerL1", "ActivePowerL2", "ActivePowerL3"); - } else if (i.includes("SymmetricEssNature")) { - channels.push("ActivePower"); - } - } - // Meter - if (i.includes("MeterNature")) { - if (i.includes("AsymmetricMeterNature") && !ignoreNatures["AsymmetricMeterNature"]) { - channels.push("ActivePowerL1", "ActivePowerL2", "ActivePowerL3"); - } else if (i.includes("SymmetricMeterNature")) { - channels.push("ActivePower"); - } - } - // Charger - if (i.includes("ChargerNature")) { - channels.push("ActualPower"); - } - // store result - if (channels.length > 0) { - result[thingId] = channels; - } - } - return result; - } - - /** - * Returns ChannelAddresses of ESS Soc channels - */ - public getEssSocChannels(): DefaultTypes.ChannelAddresses { - let result: DefaultTypes.ChannelAddresses = {} - for (let thingId of this.storageThings) { - let channels = []; - // ESS - channels.push("Soc"); - // store result - if (channels.length > 0) { - result[thingId] = channels; - } - } - return result; - } - - /** - * Returns ChannelAddresses required by EVCS widget - */ - private getEvcsWidgetChannels(): DefaultTypes.ChannelAddresses { - let result: DefaultTypes.ChannelAddresses = {} - for (let thingId of this.evcsDevices) { - result[thingId] = ["State", "Plug", "CurrUser", "ActualPower", "EnergySession", "EnergyTotal"]; - } - return result; - } - - /** - * Return ChannelAddresses of power and soc channels - */ - public getImportantChannels(): DefaultTypes.ChannelAddresses { - let channels: DefaultTypes.ChannelAddresses = {}; - function merge(obj: DefaultTypes.ChannelAddresses) { - for (let thing in obj) { - if (thing in channels) { - channels[thing] = channels[thing].concat(obj[thing]); - } else { - channels[thing] = obj[thing]; - } - } - } - // basic channels - merge(this.getStateChannels()); - merge(this.getPowerChannels()); - merge(this.getEssSocChannels()); - // widget channels - merge(this.getEvcsWidgetChannels()); - return channels; - } - - public getWidgets(): Widget[] { - let widgets: Widget[] = []; - if (this.evcsDevices.length > 0) { - widgets.push("EVCS"); - } - return widgets; - } - - private getImplements(thing: DefaultTypes.ThingConfig): string | string[] { - if (thing.class in this.meta) { // TODO casting - // get implements from meta - return this.meta[thing.class].implements; - } else { - // use class - return thing.class; - } - } +import { DefaultTypes } from '../service/defaulttypes' +import { Role } from '../type/role' +import { Widget } from '../type/widget' + +export class ConfigImpl implements DefaultTypes.Config { + + // Attributes from Config interface + public readonly things: { + [id: string]: { + id: string, + alias: string, + class: string | string[], + [channel: string]: any + } + }; + public readonly meta: { + [clazz: string]: { + implements: string[], + channels: { + [channel: string]: { + name: string, + title: string, + type: string | string[], + optional: boolean, + array: boolean, + readRoles: Role[], + writeRoles: Role[], + defaultValue: string + } + } + } + }; + + // A list of thing ids which are matching Natures. (e.g. ["ess0", "ess1"]) + public readonly storageThings: string[] = []; + public readonly chargers: string[] = []; + public readonly gridMeters: string[] = []; + public readonly productionMeters: string[] = []; + public readonly consumptionMeters: string[] = []; + public readonly otherMeters: string[] = []; // TODO show otherMeters in Energymonitor + public readonly bridges: string[] = []; + public readonly scheduler: string = null; + public readonly controllers: string[] = []; + public readonly persistences: string[] = []; + public readonly simulatorDevices: string[] = []; + public readonly evcsDevices: string[] = []; + + constructor(private readonly config: DefaultTypes.Config) { + // convert role-strings to Role-objects + for (let clazz in config.meta) { + for (let channel in config.meta[clazz].channels) { + let roles: Role[] = []; + for (let roleString of config.meta[clazz].channels[channel].readRoles) { + roles.push(Role.getRole("" + roleString /* convert to string */)); + } + config.meta[clazz].channels[channel].readRoles = roles; + } + } + + Object.assign(this, config); + + let storageThings: string[] = [] + let chargers: string[] = []; + let gridMeters: string[] = []; + let productionMeters: string[] = []; + let consumptionMeters: string[] = []; + let otherMeters: string[] = []; + let bridges: string[] = []; + let scheduler: string = null; + let controllers: string[] = []; + let persistences: string[] = []; + let simulatorDevices: string[] = []; + let evcsDevices: string[] = []; + + for (let thingId in config.things) { + let thing = config.things[thingId]; + let i = this.getImplements(thing); + + /* + * Natures + */ + // Ess + if (i.includes("EssNature") + && !i.includes("EssClusterNature") /* ignore cluster */ + && !i.includes("AsymmetricSymmetricCombinationEssNature") /* ignore symmetric Ess of Pro 9-12 */) { + storageThings.push(thingId); + } + // Meter + if (i.includes("MeterNature")) { + if ("type" in thing) { + if (thing.type == 'grid') { + gridMeters.push(thingId); + } else if (thing.type === "production") { + productionMeters.push(thingId); + } else if (thing.type === "consumption") { + consumptionMeters.push(thingId); + } else { + otherMeters.push(thingId); + } + } + } + // Charger + if (i.includes("ChargerNature")) { + productionMeters.push(thingId); + chargers.push(thingId); + } + /* + * Other Things + */ + // Bridge + if (i.includes("io.openems.api.bridge.Bridge")) { + bridges.push(thingId); + } + // Scheduler + if (i.includes("io.openems.api.scheduler.Scheduler")) { + scheduler = thingId; + } + // Controller + if (i.includes("io.openems.api.controller.Controller")) { + controllers.push(thingId); + } + // Persistence + if (i.includes("io.openems.api.persistence.Persistence")) { + persistences.push(thingId); + } + // Simulator Devices + if (i.includes("io.openems.impl.device.simulator.Simulator")) { + simulatorDevices.push(thingId); + } + // Simulator Devices + if (i.includes("KebaDeviceNature")) { + evcsDevices.push(thingId); + } + } + + this.storageThings = storageThings.sort(); + this.chargers = chargers.sort(); + this.gridMeters = gridMeters.sort(); + this.productionMeters = productionMeters.sort(); + this.bridges = bridges.sort(); + this.scheduler = scheduler; + this.controllers = controllers; + this.persistences = persistences; + this.simulatorDevices = simulatorDevices; + this.evcsDevices = evcsDevices; + } + + public getStateChannels(): DefaultTypes.ChannelAddresses { + let result: DefaultTypes.ChannelAddresses = {} + + // Set "ignoreNatures" + for (let thingId in this.config.things) { + result[thingId] = ["State"]; + } + return result; + } + + + /** + * Return ChannelAddresses of power channels + */ + public getPowerChannels(): DefaultTypes.ChannelAddresses { + let ignoreNatures = { EssClusterNature: true }; + let result: DefaultTypes.ChannelAddresses = {} + + // Set "ignoreNatures" + for (let thingId of this.storageThings) { + let i = this.getImplements(this.config.things[thingId]); + + if (i.includes("FeneconCommercialEss")) { // workaround to ignore asymmetric meter for commercial + ignoreNatures["AsymmetricMeterNature"] = true; + } + } + // Parse all things + for (let thingId in this.config.things) { + let clazz = this.config.things[thingId].class; // TODO casting + let i = this.getImplements(this.config.things[thingId]); + let channels = []; + // ESS + if (i.includes("EssNature") + && !i.includes("EssClusterNature") /* ignore cluster */ + && !i.includes("AsymmetricSymmetricCombinationEssNature") /* ignore symmetric Ess of Pro 9-12 */) { + if (i.includes("FeneconMiniEss")) { + channels.push("ActivePowerL1"); + } else if (i.includes("AsymmetricEssNature")) { + channels.push("ActivePowerL1", "ActivePowerL2", "ActivePowerL3"); + } else if (i.includes("SymmetricEssNature")) { + channels.push("ActivePower"); + } + } + // Meter + if (i.includes("MeterNature")) { + if (i.includes("AsymmetricMeterNature") && !ignoreNatures["AsymmetricMeterNature"]) { + channels.push("ActivePowerL1", "ActivePowerL2", "ActivePowerL3"); + } else if (i.includes("SymmetricMeterNature")) { + channels.push("ActivePower"); + } + } + // Charger + if (i.includes("ChargerNature")) { + channels.push("ActualPower"); + } + // store result + if (channels.length > 0) { + result[thingId] = channels; + } + } + return result; + } + + /** + * Returns ChannelAddresses of ESS Soc channels + */ + public getEssSocChannels(): DefaultTypes.ChannelAddresses { + let result: DefaultTypes.ChannelAddresses = {} + for (let thingId of this.storageThings) { + let channels = []; + // ESS + channels.push("Soc"); + // store result + if (channels.length > 0) { + result[thingId] = channels; + } + } + return result; + } + + /** + * Returns ChannelAddresses required by EVCS widget + */ + private getEvcsWidgetChannels(): DefaultTypes.ChannelAddresses { + let result: DefaultTypes.ChannelAddresses = {} + for (let thingId of this.evcsDevices) { + result[thingId] = ["State", "Plug", "CurrUser", "ActualPower", "EnergySession", "EnergyTotal"]; + } + return result; + } + + /** + * Return ChannelAddresses of power and soc channels + */ + public getImportantChannels(): DefaultTypes.ChannelAddresses { + let channels: DefaultTypes.ChannelAddresses = {}; + function merge(obj: DefaultTypes.ChannelAddresses) { + for (let thing in obj) { + if (thing in channels) { + channels[thing] = channels[thing].concat(obj[thing]); + } else { + channels[thing] = obj[thing]; + } + } + } + // basic channels + merge(this.getStateChannels()); + merge(this.getPowerChannels()); + merge(this.getEssSocChannels()); + // widget channels + merge(this.getEvcsWidgetChannels()); + return channels; + } + + public getWidgets(): Widget[] { + let widgets: Widget[] = []; + if (this.evcsDevices.length > 0) { + widgets.push("EVCS"); + } + return widgets; + } + + private getImplements(thing: DefaultTypes.ThingConfig): string | string[] { + if (thing.class in this.meta) { // TODO casting + // get implements from meta + return this.meta[thing.class].implements; + } else { + // use class + return thing.class; + } + } } \ 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 6a901e3fe12..41b5f53a0de 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -1,325 +1,325 @@ -import { Injectable, EventEmitter } from '@angular/core'; -import { Subject } from 'rxjs/Subject'; -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { Router, ActivatedRoute, Params } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; -import { Subscription } from 'rxjs/Subscription'; -import websocketConnect from 'rxjs-websockets'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/timeout'; -import 'rxjs/add/operator/takeUntil'; - -import { environment as env } from '../../../environments'; -import { Service } from './service'; -import { Utils } from './utils'; -import { Device } from '../device/device'; -import { Role } from '../type/role'; -import { DefaultTypes } from '../service/defaulttypes'; -import { DefaultMessages } from '../service/defaultmessages'; - -@Injectable() -export class Websocket { - public static readonly TIMEOUT = 5000; - private static readonly DEFAULT_EDGEID = 0; - private static readonly DEFAULT_DEVICENAME = "fems"; - - // holds references of device names (=key) to Device objects (=value) - private _devices: BehaviorSubject<{ [name: string]: Device }> = new BehaviorSubject({}); - public get devices() { - return this._devices; - } - - // holds the currently selected device - private _currentDevice: BehaviorSubject = new BehaviorSubject(null); - public get currentDevice() { - return this._currentDevice; - } - - public status: DefaultTypes.ConnectionStatus = "connecting"; - public isWebsocketConnected: BehaviorSubject = new BehaviorSubject(false); - - private username: string = ""; - // private messages: Observable; - private messageSubscription: Subscription = null; - private inputStream: Subject; - private queryreply = new Subject<{ id: string[] }>(); - private stopOnInitialize: Subject = new Subject(); - - // holds stream per device (=key1) and message-id (=key2); triggered on message reply for the device - private replyStreams: { [deviceName: string]: { [messageId: string]: Subject } } = {}; - - // tracks which message id (=key) is connected with which deviceName (=value) - private pendingQueryReplies: { [id: string]: string } = {}; - - constructor( - private router: Router, - private service: Service, - ) { - // try to auto connect using token or session_id - setTimeout(() => { - this.connect(); - }) - } - - /** - * Parses the route params and sets the current device - */ - public setCurrentDevice(route: ActivatedRoute): Subject { - let onTimeout = () => { - // Timeout: redirect to overview - this.router.navigate(['/overview']); - subscription.unsubscribe(); - } - - let deviceName = route.snapshot.params["device"]; - let subscription = this.devices - .filter(devices => deviceName in devices) - .first() - .map(devices => devices[deviceName]) - .subscribe(device => { - if (device == null || !device.online) { - onTimeout(); - } else { - // set current device - this.currentDevice.next(device); - device.markAsCurrentDevice(); - } - }, error => { - console.error("Error while setting current device: ", error); - }) - setTimeout(() => { - let device = this.currentDevice.getValue(); - if (device == null || !device.online) { - onTimeout(); - } - }, Websocket.TIMEOUT); - return this.currentDevice; - } - - /** - * Clears the current device - */ - public clearCurrentDevice() { - this.currentDevice.next(null); - } - - /** - * Opens a connection using a stored token or a cookie with a session_id for this websocket. Called once by constructor - */ - private connect(): BehaviorSubject { - if (this.messageSubscription != null) { - return; - } - - if (env.debugMode) { - console.info("Websocket connect to URL [" + env.url + "]"); - } - const { messages, connectionStatus } = websocketConnect( - env.url, - this.inputStream = new Subject() - ); - connectionStatus - .takeUntil(this.stopOnInitialize) - .subscribe(count => { - let isConnected = count > 0; - if (env.debugMode) { - console.info("Websocket connected [" + isConnected + "]"); - } - this.isWebsocketConnected.next(isConnected); - - if (!isConnected && this.status == 'online') { - this.service.notify({ - message: "Connection lost. Trying to reconnect.", // TODO translate - type: 'warning' - }); - // TODO show spinners everywhere - this.status = 'connecting'; - } - - if (isConnected) { - this.status = 'waiting for authentication'; - } - }, error => { - console.error(error); - }, () => { - this.isWebsocketConnected.next(false); - }); - this.messageSubscription = messages.takeUntil(this.stopOnInitialize).retryWhen(errors => { - return errors.delay(1000); - - }).map(message => JSON.parse(message)).subscribe(message => { - // called on every receive of message from server - if (env.debugMode) { - console.info("RECV", message); - } - /* - * Authenticate - */ - if ("authenticate" in message && "mode" in message.authenticate) { - let mode = message.authenticate.mode; - - if (mode === "allow") { - // authentication successful - this.status = "online"; - - if ("token" in message.authenticate) { - // received login token -> save in cookie - this.service.setToken(message.authenticate.token); - } - - } else { - // authentication denied -> close websocket - this.status = "failed"; - this.service.removeToken(); - this.initialize(); - if (env.backend === "OpenEMS Backend") { - if (env.production) { - window.location.href = "/web/login?redirect=/m/overview"; - } else { - console.info("would redirect..."); - } - } else if (env.backend === "OpenEMS Edge") { - this.router.navigate(['/overview']); - } - } - } - - /* - * Query reply - */ - if ("messageId" in message && "ui" in message.messageId) { - // Receive a reply with a message id -> find device and forward to devices' replyStream - let messageId = message.messageId.ui; - for (let deviceName in this.replyStreams) { - if (messageId in this.replyStreams[deviceName]) { - this.replyStreams[deviceName][messageId].next(message); - break; - } - } - } - - /* - * Metadata - */ - if ("metadata" in message) { - if ("edges" in message.metadata) { - let devices = message.metadata.edges; - let newDevices = {}; - for (let device of devices) { - let replyStream: { [messageId: string]: Subject } = {}; - this.replyStreams[device.name] = replyStream; - let newDevice = new Device( - device.id, - device.name, - device.comment, - device.producttype, - Role.getRole(device.role), - device.online, - replyStream, - this - ); - newDevices[newDevice.name] = newDevice; - } - this.devices.next(newDevices); - } - } - - /* - * receive notification - */ - if ("notification" in message) { - let notification = message.notification; - let n: DefaultTypes.Notification; - let notify: boolean = true; - if ("code" in notification) { - // handle specific notification codes - see Java source for details - let code = notification.code; - let params = notification.params; - if (code == 100 /* device disconnected -> mark as offline */) { - let deviceId = params[0]; - if (deviceId in this.devices.getValue()) { - this.devices.getValue()[deviceId].setOnline(false); - } - } else if (code == 101 /* device reconnected -> mark as online */) { - let deviceId = params[0]; - if (deviceId in this.devices.getValue()) { - let device = this.devices.getValue()[deviceId]; - device.setOnline(true); - } - } else if (code == 103 /* authentication by token failed */) { - let token: string = params[0]; - if (token !== "") { - // remove old token - this.service.removeToken(); - } - // ask for authentication info - this.status = "waiting for authentication"; - notify = false; - setTimeout(() => { - this.clearCurrentDevice(); - this.router.navigate(["/overview"]); - }); - } - } - if (notify) { - this.service.notify(notification); - } - } - }); - return this.isWebsocketConnected; - } - - /** - * Reset everything to default - */ - private initialize() { - // TODO do not stop the websocket connection on logout - this.stopOnInitialize.next(); - this.stopOnInitialize.complete(); - this.messageSubscription.unsubscribe(); - this.messageSubscription = null; - this.devices.next({}); - } - - /** - * Opens the websocket and logs in - */ - public logIn(password: string) { - if (this.isWebsocketConnected.getValue()) { - // websocket was connected - this.send(DefaultMessages.authenticateLogin(password)); - } else { - // websocket was NOT connected - this.connect() - .takeUntil(this.stopOnInitialize) - .filter(isConnected => isConnected) - .first() - .subscribe(isConnected => { - setTimeout(() => { - this.send(DefaultMessages.authenticateLogin(password)) - }, 500); - }); - } - } - - /** - * Logs out and closes the websocket - */ - public logOut() { - // TODO this is kind of working for now... better would be to not close the websocket but to handle session validity serverside - this.send(DefaultMessages.authenticateLogout()); - this.status = "waiting for authentication"; - this.service.removeToken(); - this.initialize(); - } - - /** - * Sends a message to the websocket - */ - public send(message: any): void { - if (env.debugMode) { - console.info("SEND: ", message); - } - this.inputStream.next(JSON.stringify(message)); - } +import { Injectable, EventEmitter } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Router, ActivatedRoute, Params } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { Subscription } from 'rxjs/Subscription'; +import websocketConnect from 'rxjs-websockets'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/timeout'; +import 'rxjs/add/operator/takeUntil'; + +import { environment as env } from '../../../environments'; +import { Service } from './service'; +import { Utils } from './utils'; +import { Device } from '../device/device'; +import { Role } from '../type/role'; +import { DefaultTypes } from '../service/defaulttypes'; +import { DefaultMessages } from '../service/defaultmessages'; + +@Injectable() +export class Websocket { + public static readonly TIMEOUT = 15000; + private static readonly DEFAULT_EDGEID = 0; + private static readonly DEFAULT_DEVICENAME = "fems"; + + // holds references of device names (=key) to Device objects (=value) + private _devices: BehaviorSubject<{ [name: string]: Device }> = new BehaviorSubject({}); + public get devices() { + return this._devices; + } + + // holds the currently selected device + private _currentDevice: BehaviorSubject = new BehaviorSubject(null); + public get currentDevice() { + return this._currentDevice; + } + + public status: DefaultTypes.ConnectionStatus = "connecting"; + public isWebsocketConnected: BehaviorSubject = new BehaviorSubject(false); + + private username: string = ""; + // private messages: Observable; + private messageSubscription: Subscription = null; + private inputStream: Subject; + private queryreply = new Subject<{ id: string[] }>(); + private stopOnInitialize: Subject = new Subject(); + + // holds stream per device (=key1) and message-id (=key2); triggered on message reply for the device + private replyStreams: { [deviceName: string]: { [messageId: string]: Subject } } = {}; + + // tracks which message id (=key) is connected with which deviceName (=value) + private pendingQueryReplies: { [id: string]: string } = {}; + + constructor( + private router: Router, + private service: Service, + ) { + // try to auto connect using token or session_id + setTimeout(() => { + this.connect(); + }) + } + + /** + * Parses the route params and sets the current device + */ + public setCurrentDevice(route: ActivatedRoute): Subject { + let onTimeout = () => { + // Timeout: redirect to overview + this.router.navigate(['/overview']); + subscription.unsubscribe(); + } + + let deviceName = route.snapshot.params["device"]; + let subscription = this.devices + .filter(devices => deviceName in devices) + .first() + .map(devices => devices[deviceName]) + .subscribe(device => { + if (device == null || !device.online) { + onTimeout(); + } else { + // set current device + this.currentDevice.next(device); + device.markAsCurrentDevice(); + } + }, error => { + console.error("Error while setting current device: ", error); + }) + setTimeout(() => { + let device = this.currentDevice.getValue(); + if (device == null || !device.online) { + onTimeout(); + } + }, Websocket.TIMEOUT); + return this.currentDevice; + } + + /** + * Clears the current device + */ + public clearCurrentDevice() { + this.currentDevice.next(null); + } + + /** + * Opens a connection using a stored token or a cookie with a session_id for this websocket. Called once by constructor + */ + private connect(): BehaviorSubject { + if (this.messageSubscription != null) { + return; + } + + if (env.debugMode) { + console.info("Websocket connect to URL [" + env.url + "]"); + } + const { messages, connectionStatus } = websocketConnect( + env.url, + this.inputStream = new Subject() + ); + connectionStatus + .takeUntil(this.stopOnInitialize) + .subscribe(count => { + let isConnected = count > 0; + if (env.debugMode) { + console.info("Websocket connected [" + isConnected + "]"); + } + this.isWebsocketConnected.next(isConnected); + + if (!isConnected && this.status == 'online') { + this.service.notify({ + message: "Connection lost. Trying to reconnect.", // TODO translate + type: 'warning' + }); + // TODO show spinners everywhere + this.status = 'connecting'; + } + + if (isConnected) { + this.status = 'waiting for authentication'; + } + }, error => { + console.error(error); + }, () => { + this.isWebsocketConnected.next(false); + }); + this.messageSubscription = messages.takeUntil(this.stopOnInitialize).retryWhen(errors => { + return errors.delay(1000); + + }).map(message => JSON.parse(message)).subscribe(message => { + // called on every receive of message from server + if (env.debugMode) { + console.info("RECV", message); + } + /* + * Authenticate + */ + if ("authenticate" in message && "mode" in message.authenticate) { + let mode = message.authenticate.mode; + + if (mode === "allow") { + // authentication successful + this.status = "online"; + + if ("token" in message.authenticate) { + // received login token -> save in cookie + this.service.setToken(message.authenticate.token); + } + + } else { + // authentication denied -> close websocket + this.status = "failed"; + this.service.removeToken(); + this.initialize(); + if (env.backend === "OpenEMS Backend") { + if (env.production) { + window.location.href = "/web/login?redirect=/m/overview"; + } else { + console.info("would redirect..."); + } + } else if (env.backend === "OpenEMS Edge") { + this.router.navigate(['/overview']); + } + } + } + + /* + * Query reply + */ + if ("messageId" in message && "ui" in message.messageId) { + // Receive a reply with a message id -> find device and forward to devices' replyStream + let messageId = message.messageId.ui; + for (let deviceName in this.replyStreams) { + if (messageId in this.replyStreams[deviceName]) { + this.replyStreams[deviceName][messageId].next(message); + break; + } + } + } + + /* + * Metadata + */ + if ("metadata" in message) { + if ("edges" in message.metadata) { + let devices = message.metadata.edges; + let newDevices = {}; + for (let device of devices) { + let replyStream: { [messageId: string]: Subject } = {}; + this.replyStreams[device.name] = replyStream; + let newDevice = new Device( + device.id, + device.name, + device.comment, + device.producttype, + Role.getRole(device.role), + device.online, + replyStream, + this + ); + newDevices[newDevice.name] = newDevice; + } + this.devices.next(newDevices); + } + } + + /* + * receive notification + */ + if ("notification" in message) { + let notification = message.notification; + let n: DefaultTypes.Notification; + let notify: boolean = true; + if ("code" in notification) { + // handle specific notification codes - see Java source for details + let code = notification.code; + let params = notification.params; + if (code == 100 /* device disconnected -> mark as offline */) { + let deviceId = params[0]; + if (deviceId in this.devices.getValue()) { + this.devices.getValue()[deviceId].setOnline(false); + } + } else if (code == 101 /* device reconnected -> mark as online */) { + let deviceId = params[0]; + if (deviceId in this.devices.getValue()) { + let device = this.devices.getValue()[deviceId]; + device.setOnline(true); + } + } else if (code == 103 /* authentication by token failed */) { + let token: string = params[0]; + if (token !== "") { + // remove old token + this.service.removeToken(); + } + // ask for authentication info + this.status = "waiting for authentication"; + notify = false; + setTimeout(() => { + this.clearCurrentDevice(); + this.router.navigate(["/overview"]); + }); + } + } + if (notify) { + this.service.notify(notification); + } + } + }); + return this.isWebsocketConnected; + } + + /** + * Reset everything to default + */ + private initialize() { + // TODO do not stop the websocket connection on logout + this.stopOnInitialize.next(); + this.stopOnInitialize.complete(); + this.messageSubscription.unsubscribe(); + this.messageSubscription = null; + this.devices.next({}); + } + + /** + * Opens the websocket and logs in + */ + public logIn(password: string) { + if (this.isWebsocketConnected.getValue()) { + // websocket was connected + this.send(DefaultMessages.authenticateLogin(password)); + } else { + // websocket was NOT connected + this.connect() + .takeUntil(this.stopOnInitialize) + .filter(isConnected => isConnected) + .first() + .subscribe(isConnected => { + setTimeout(() => { + this.send(DefaultMessages.authenticateLogin(password)) + }, 500); + }); + } + } + + /** + * Logs out and closes the websocket + */ + public logOut() { + // TODO this is kind of working for now... better would be to not close the websocket but to handle session validity serverside + this.send(DefaultMessages.authenticateLogout()); + this.status = "waiting for authentication"; + this.service.removeToken(); + this.initialize(); + } + + /** + * Sends a message to the websocket + */ + public send(message: any): void { + if (env.debugMode) { + console.info("SEND: ", message); + } + this.inputStream.next(JSON.stringify(message)); + } } \ No newline at end of file From 4a75206e253c2936fa9eb2f2b52e93fd83bda6b3 Mon Sep 17 00:00:00 2001 From: Stefan Feilmeier Date: Wed, 14 Mar 2018 23:47:41 +0100 Subject: [PATCH 156/156] Use new backend path for production --- ui/src/environments/openems-backend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/environments/openems-backend.ts b/ui/src/environments/openems-backend.ts index 2ea5724ae27..61515e16567 100644 --- a/ui/src/environments/openems-backend.ts +++ b/ui/src/environments/openems-backend.ts @@ -4,7 +4,7 @@ import { DefaultTypes } from '../app/shared/service/defaulttypes'; class OpenemsBackendEnvironment extends Environment { public readonly production = true; public readonly url = (location.protocol == "https:" ? "wss" : "ws") + - "://" + location.hostname + ":" + location.port + "/openems-backend-ui"; + "://" + location.hostname + ":" + location.port + "/openems-backend-ui2"; public readonly backend: DefaultTypes.Backend = "OpenEMS Backend"; }

    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�w6sZ&R~*^r)6$M0~_a0^}n3|>`JMA@*N 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