コード例 #1
0
ファイル: Program.cs プロジェクト: barnstee/pv-monitor
        static async Task Main(string[] args)
        {
#if DEBUG
            // Attach remote debugger
            while (true)
            {
                Console.WriteLine("Waiting for remote debugger to attach...");

                if (Debugger.IsAttached)
                {
                    break;
                }

                System.Threading.Thread.Sleep(1000);
            }
#endif
            // init log file
            Log.Logger = new LoggerConfiguration()
                         .WriteTo.Console()
                         .WriteTo.File(
                "logfile.txt",
                fileSizeLimitBytes: 1024 * 1024,
                flushToDiskInterval: TimeSpan.FromSeconds(30),
                rollOnFileSizeLimit: true,
                retainedFileCountLimit: 2)
                         .MinimumLevel.Debug()
                         .CreateLogger();
            Log.Information($"{Assembly.GetExecutingAssembly()} V{FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion}");

            // init Modbus TCP client for wallbox
            ModbusTCPClient wallbox = new ModbusTCPClient();
            wallbox.Connect(WallbeWallboxBaseAddress, WallbeWallboxModbusTCPPort);

            // init Modbus TCP client for inverter
            ModbusTCPClient inverter = new ModbusTCPClient();
            inverter.Connect(FroniusInverterBaseAddress, FroniusInverterModbusTCPPort);

            // read current inverter power limit (percentage)
            byte[] WMaxLimit = inverter.Read(
                FroniusInverterModbusUnitID,
                ModbusTCPClient.FunctionCode.ReadHoldingRegisters,
                SunSpecInverterModbusRegisterMapFloat.InverterBaseAddress + SunSpecInverterModbusRegisterMapFloat.WMaxLimPctOffset,
                SunSpecInverterModbusRegisterMapFloat.WMaxLimPctLength);

            int existingLimitPercent = Utils.ByteSwap(BitConverter.ToUInt16(WMaxLimit)) / 100;

            // go to the maximum grid export power limit with immediate effect without timeout
            ushort InverterPowerOutputPercent = (ushort)((GridExportPowerLimit / FroniusSymoMaxPower) * 100);
            inverter.WriteHoldingRegisters(
                FroniusInverterModbusUnitID,
                SunSpecInverterModbusRegisterMapFloat.InverterBaseAddress + SunSpecInverterModbusRegisterMapFloat.WMaxLimPctOffset,
                new ushort[] { (ushort)(InverterPowerOutputPercent * 100), 0, 0, 0, 1 });

            // check new setting
            WMaxLimit = inverter.Read(
                FroniusInverterModbusUnitID,
                ModbusTCPClient.FunctionCode.ReadHoldingRegisters,
                SunSpecInverterModbusRegisterMapFloat.InverterBaseAddress + SunSpecInverterModbusRegisterMapFloat.WMaxLimPctOffset,
                SunSpecInverterModbusRegisterMapFloat.WMaxLimPctLength);

            int newLimitPercent = Utils.ByteSwap(BitConverter.ToUInt16(WMaxLimit)) / 100;

            // print a list of all available serial ports for convenience
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                Log.Information("Serial port available: " + port);
            }

            // start processing smart meter messages
            SmartMessageLanguage sml = new SmartMessageLanguage(LinuxUSBSerialPort);
            sml.ProcessStream();

            DeviceClient deviceClient = null;
            try
            {
                // register the device
                string scopeId      = "0ne0010B637";
                string deviceId     = "RasPi2B";
                string primaryKey   = "";
                string secondaryKey = "";

                var security  = new SecurityProviderSymmetricKey(deviceId, primaryKey, secondaryKey);
                var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.TcpWithWebSocketFallback);

                var provisioningClient = ProvisioningDeviceClient.Create("global.azure-devices-provisioning.net", scopeId, security, transport);
                var result             = await provisioningClient.RegisterAsync();

                var connectionString = "HostName=" + result.AssignedHub + ";DeviceId=" + result.DeviceId + ";SharedAccessKey=" + primaryKey;
                deviceClient = DeviceClient.CreateFromConnectionString(connectionString, TransportType.Mqtt);

                // register our methods
                await deviceClient.SetMethodHandlerAsync("ChargeNowToggle", ChargeNowHandler, null);

                await deviceClient.SetMethodHandlerAsync("ChargingPhases", ChargingPhasesHandler, null);
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Registering device failed!");
            }

            TelemetryData telemetryData = new TelemetryData();
            while (true)
            {
                telemetryData.ChargeNow         = _chargeNow;
                telemetryData.NumChargingPhases = _chargingPhases;

                try
                {
                    // read the current weather data from web service
                    WebClient webClient = new WebClient
                    {
                        BaseAddress = "https://api.openweathermap.org/"
                    };

                    string      json    = webClient.DownloadString("data/2.5/weather?q=Munich,de&units=metric&appid=2898258e654f7f321ef3589c4fa58a9b");
                    WeatherInfo weather = JsonConvert.DeserializeObject <WeatherInfo>(json);
                    if (weather != null)
                    {
                        telemetryData.Temperature = weather.main.temp;
                        telemetryData.WindSpeed   = weather.wind.speed;
                        telemetryData.CloudCover  = weather.weather[0].description;
                    }

                    webClient.Dispose();
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Getting weather data failed!");
                }

                try
                {
                    // read the current forecast data from web service
                    WebClient webClient = new WebClient
                    {
                        BaseAddress = "https://api.openweathermap.org/"
                    };

                    string   json     = webClient.DownloadString("data/2.5/forecast?q=Munich,de&units=metric&appid=2898258e654f7f321ef3589c4fa58a9b");
                    Forecast forecast = JsonConvert.DeserializeObject <Forecast>(json);
                    if (forecast != null && forecast.list != null && forecast.list.Count == 40)
                    {
                        telemetryData.CloudinessForecast = string.Empty;
                        for (int i = 0; i < 40; i++)
                        {
                            telemetryData.CloudinessForecast += "Cloudiness on " + forecast.list[i].dt_txt + ": " + forecast.list[i].clouds.all + "%\r\n";
                        }
                    }

                    webClient.Dispose();
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Getting weather forecast failed!");
                }

                try
                {
                    // read the current converter data from web service
                    WebClient webClient = new WebClient
                    {
                        BaseAddress = "http://" + FroniusInverterBaseAddress
                    };

                    string        json      = webClient.DownloadString("solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceID=1&DataCollection=CommonInverterData");
                    DCACConverter converter = JsonConvert.DeserializeObject <DCACConverter>(json);
                    if (converter != null)
                    {
                        if (converter.Body.Data.PAC != null)
                        {
                            telemetryData.PVOutputPower = converter.Body.Data.PAC.Value;
                        }
                        if (converter.Body.Data.DAY_ENERGY != null)
                        {
                            telemetryData.PVOutputEnergyDay = ((double)converter.Body.Data.DAY_ENERGY.Value) / 1000.0;
                        }
                        if (converter.Body.Data.YEAR_ENERGY != null)
                        {
                            telemetryData.PVOutputEnergyYear = ((double)converter.Body.Data.YEAR_ENERGY.Value) / 1000.0;
                        }
                        if (converter.Body.Data.TOTAL_ENERGY != null)
                        {
                            telemetryData.PVOutputEnergyTotal = ((double)converter.Body.Data.TOTAL_ENERGY.Value) / 1000.0;
                        }
                    }

                    webClient.Dispose();
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Getting converter data failed!");
                }

                try
                {
                    // read the current smart meter data
                    telemetryData.MeterEnergyPurchased = sml.Meter.EnergyPurchased;
                    telemetryData.MeterEnergySold      = sml.Meter.EnergySold;
                    telemetryData.CurrentPower         = sml.Meter.CurrentPower;

                    telemetryData.EnergyCost   = telemetryData.MeterEnergyPurchased * KWhCost;
                    telemetryData.EnergyProfit = telemetryData.MeterEnergySold * KWhProfit;

                    // calculate energy consumed from the other telemetry, if available
                    telemetryData.MeterEnergyConsumed = 0.0;
                    if ((telemetryData.MeterEnergyPurchased != 0.0) &&
                        (telemetryData.MeterEnergySold != 0.0) &&
                        (telemetryData.PVOutputEnergyTotal != 0.0))
                    {
                        telemetryData.MeterEnergyConsumed  = telemetryData.PVOutputEnergyTotal + sml.Meter.EnergyPurchased - sml.Meter.EnergySold;
                        telemetryData.CurrentPowerConsumed = telemetryData.PVOutputPower + sml.Meter.CurrentPower;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Getting smart meter data failed!");
                }

                try
                {
                    // ramp up or down EV charging, based on surplus
                    bool chargingInProgress = IsEVChargingInProgress(wallbox);
                    telemetryData.EVChargingInProgress = chargingInProgress? 1 : 0;
                    if (chargingInProgress)
                    {
                        // read current current (in Amps)
                        ushort wallbeWallboxCurrentCurrentSetting = Utils.ByteSwap(BitConverter.ToUInt16(wallbox.Read(
                                                                                                             WallbeWallboxModbusUnitID,
                                                                                                             ModbusTCPClient.FunctionCode.ReadHoldingRegisters,
                                                                                                             WallbeWallboxCurrentCurrentSettingAddress,
                                                                                                             1)));
                        telemetryData.WallboxCurrent = wallbeWallboxCurrentCurrentSetting;

                        OptimizeEVCharging(wallbox, sml.Meter.CurrentPower);
                    }
                    else
                    {
                        telemetryData.WallboxCurrent = 0;

                        // check if we should start charging our EV with the surplus power, but we need at least 6A of current per charing phase
                        // or the user set the "charge now" flag via direct method
                        if (((sml.Meter.CurrentPower / 230) < (_chargingPhases * -6.0f)) || _chargeNow)
                        {
                            StartEVCharging(wallbox);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "EV charing control failed!");
                }

                try
                {
                    string  messageString = JsonConvert.SerializeObject(telemetryData);
                    Message cloudMessage  = new Message(Encoding.UTF8.GetBytes(messageString));

                    await deviceClient.SendEventAsync(cloudMessage);

                    Debug.WriteLine("{0}: {1}", DateTime.Now, messageString);
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Sending telemetry failed!");
                }

                // wait 5 seconds and go again
                await Task.Delay(5000).ConfigureAwait(false);
            }
        }
コード例 #2
0
        static async Task Main(string[] args)
        {
#if DEBUG
            // Attach remote debugger
            while (true)
            {
                Console.WriteLine("Waiting for remote debugger to attach...");

                if (Debugger.IsAttached)
                {
                    break;
                }

                System.Threading.Thread.Sleep(1000);
            }
#endif
            // print a list of all available serial ports for convenience
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                Console.WriteLine("Serial port available: " + port);
            }

            // start processing smart meter messages
            SmartMeterLanguage sml = new SmartMeterLanguage(LinuxUSBSerialPort);
            sml.ProcessStream();

            DeviceClient deviceClient = null;
            try
            {
                // register the device
                string scopeId      = "0ne0010B637";
                string deviceId     = "RasPi2B";
                string primaryKey   = "";
                string secondaryKey = "";

                var security  = new SecurityProviderSymmetricKey(deviceId, primaryKey, secondaryKey);
                var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.TcpWithWebSocketFallback);

                var provisioningClient = ProvisioningDeviceClient.Create("global.azure-devices-provisioning.net", scopeId, security, transport);
                var result             = await provisioningClient.RegisterAsync();

                var connectionString = "HostName=" + result.AssignedHub + ";DeviceId=" + result.DeviceId + ";SharedAccessKey=" + primaryKey;
                deviceClient = DeviceClient.CreateFromConnectionString(connectionString, TransportType.Mqtt);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            while (true)
            {
                TelemetryData telemetryData = new TelemetryData();

                try
                {
                    // read the current weather data from web service
                    using WebClient webClient = new WebClient
                          {
                              BaseAddress = "https://api.openweathermap.org/"
                          };
                    var         json    = webClient.DownloadString("data/2.5/weather?q=Munich,de&units=metric&appid=2898258e654f7f321ef3589c4fa58a9b");
                    WeatherInfo weather = JsonConvert.DeserializeObject <WeatherInfo>(json);
                    if (weather != null)
                    {
                        telemetryData.Temperature = weather.main.temp;
                        telemetryData.WindSpeed   = weather.wind.speed;
                        telemetryData.CloudCover  = weather.weather[0].description;
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }

                try
                {
                    // read the current converter data from web service
                    using WebClient webClient = new WebClient
                          {
                              BaseAddress = "http://192.168.178.31/"
                          };
                    var           json      = webClient.DownloadString("solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceID=1&DataCollection=CommonInverterData");
                    DCACConverter converter = JsonConvert.DeserializeObject <DCACConverter>(json);
                    if (converter != null)
                    {
                        if (converter.Body.Data.PAC != null)
                        {
                            telemetryData.PVOutputPower = ((double)converter.Body.Data.PAC.Value) / 1000.0;
                        }
                        if (converter.Body.Data.DAY_ENERGY != null)
                        {
                            telemetryData.PVOutputEnergyDay = ((double)converter.Body.Data.DAY_ENERGY.Value) / 1000.0;
                        }
                        if (converter.Body.Data.YEAR_ENERGY != null)
                        {
                            telemetryData.PVOutputEnergyYear = ((double)converter.Body.Data.YEAR_ENERGY.Value) / 1000.0;
                        }
                        if (converter.Body.Data.TOTAL_ENERGY != null)
                        {
                            telemetryData.PVOutputEnergyTotal = ((double)converter.Body.Data.TOTAL_ENERGY.Value) / 1000.0;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }

                try
                {
                    // read the current smart meter data
                    if (sml != null)
                    {
                        telemetryData.MeterEnergyPurchased = sml.Meter.EnergyPurchased;
                        telemetryData.MeterEnergySold      = sml.Meter.EnergySold;
                        telemetryData.MeterEnergyConsumed  = 0.0;

                        // calculate energy consumed from the other telemetry, if available
                        if ((telemetryData.MeterEnergyPurchased != 0.0) &&
                            (telemetryData.MeterEnergySold != 0.0) &&
                            (telemetryData.PVOutputEnergyTotal != 0.0))
                        {
                            telemetryData.MeterEnergyConsumed = telemetryData.PVOutputEnergyTotal + sml.Meter.EnergyPurchased - sml.Meter.EnergySold;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }


                try
                {
                    string  messageString = JsonConvert.SerializeObject(telemetryData);
                    Message cloudMessage  = new Message(Encoding.UTF8.GetBytes(messageString));

                    await deviceClient.SendEventAsync(cloudMessage);

                    Debug.WriteLine("{0}: {1}", DateTime.Now, messageString);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }

                await Task.Delay(5000);
            }
        }