Update dbus-fzsonic skripts

This commit is contained in:
Noe 2024-12-16 15:03:27 +01:00
parent 98fb313a6f
commit 074511f6a7
35 changed files with 1097 additions and 948 deletions

View File

@ -4,6 +4,9 @@ namespace InnovEnergy.App.Backend.DataTypes;
public class Installation : TreeNode
{
//Each installation has 2 roles, a read role and a write role.
//There are 2 keys per role a public key and a secret
//Product can be 0 or 1, 0 for Salimax, 1 for Salidomo
public String Location { get; set; } = "";
public String Region { get; set; } = "";
public String Country { get; set; } = "";
@ -20,9 +23,10 @@ public class Installation : TreeNode
public String ReadRoleId { get; set; } = "";
public String WriteRoleId { get; set; } = "";
public Boolean TestingMode { get; set; } = false;
public int Status { get; set; } = -1;
public int Product { get; set; } = 0;
public int Device { get; set; } = 0;
[Ignore]
public String OrderNumbers { get; set; }
public String VrmLink { get; set; } = "";

View File

@ -282,9 +282,9 @@ public static class SessionMethods
&& Db.Delete(installation)
&& await installation.RevokeReadKey()
&& await installation.RevokeWriteKey()
&& await installation.DeleteBucket()
&& await installation.RemoveReadRole()
&& await installation.RemoveWriteRole();
&& await installation.RemoveWriteRole()
&& await installation.DeleteBucket();
}

View File

@ -7,28 +7,19 @@ namespace InnovEnergy.App.Backend.Database;
public static partial class Db
{
private static int _backupCounter = 0;
private static Boolean Insert(Object obj)
{
var success = Connection.Insert(obj) > 0;
if (success)
{
_backupCounter++;
if (_backupCounter > 100)
{
_backupCounter = 0;
BackupDatabase();
}
}
if (success) Backup();
return success;
}
public static Boolean Create(Installation installation)
{
installation.S3BucketId = Installations.Where(inst => inst.Product == installation.Product).Max(inst => (int?)inst.S3BucketId)+1 ?? 0;
// The bucket Id it calculated as follows: It is 1 + the maximum bucket id of all the existing installations of the same product
// SQLite wrapper is smart and *modifies* t's Id to the one generated (autoincrement) by the insertion
installation.S3BucketId = Installations.Where(inst => inst.Product == installation.Product).Max(inst => (int?)inst.S3BucketId)+1 ?? 0;
return Insert(installation);
}
@ -103,21 +94,8 @@ public static partial class Db
}
}
public static void UpdateAction(UserAction updatedAction)
{
var existingAction = UserActions.FirstOrDefault(action => action.Id == updatedAction.Id);
if (existingAction != null)
{
//existingAction.Description = updatedAction.Description;
//existingAction.Timestamp = updatedAction.Timestamp;
//existingAction.TestingMode = updatedAction.TestingMode;
Update(updatedAction);
Console.WriteLine("---------------Updated the Action in the database-----------------");
}
}
//This function is called from the RabbitMQ manager when a new error arrives to the database.
//We keep only the last 100 errors for each installation. If we already have stored 100 errors, we delete the older one and we insert the new one.
public static void HandleError(Error newError,int installationId)
{
@ -140,7 +118,7 @@ public static partial class Db
}
else
{
Console.WriteLine("---------------Added the new Error to the database-----------------");
Console.WriteLine("---------------Added the new Alarm to the database-----------------");
Create(newError);
}
}
@ -158,10 +136,10 @@ public static partial class Db
.OrderBy(warning => warning.Date)
.FirstOrDefault();
//Remove the old error
//Remove the old warning
Delete(oldestWarning);
//Add the new error
//Add the new warning
Create(newWarning);
}
else

View File

@ -9,6 +9,9 @@ using SQLiteConnection = SQLite.SQLiteConnection;
namespace InnovEnergy.App.Backend.Database;
//The methods of the Db class are located in multiple files (Create.cs, Read,cs, Delete.cs, Update.cs)
//That's why the class definition is partial
public static partial class Db
{
private static SQLiteConnection Connection { get; } = InitConnection();
@ -30,7 +33,7 @@ public static partial class Db
//Since this class is static, we call Init method from the Program.cs to initialize all the fields of the class
//When a class is loaded, the fields are initialized before the constructor's code is executed.
//The TableQuery fields are lazy meaning that they will be initialized when they get accessed
//The connection searches for the latest backup and it binds all the tables to it.
//The connection searches for the latest backup and binds all the tables to it.
}
//This is the constructor of the class
@ -90,7 +93,7 @@ public static partial class Db
Connection.Backup("DbBackups/" + filename);
}
//Delete all by 10 snapshots every 24 hours.
//Delete all except 10 snapshots every 24 hours.
private static async Task DeleteSnapshots()
{
while (true)

View File

@ -1,16 +1,12 @@
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations;
using Microsoft.AspNetCore.Authentication.OAuth.Claims;
namespace InnovEnergy.App.Backend.Database;
public static partial class Db
{
//Since we do not want to stress the memory in the VM a lot, we make a snapshot of the database every 100 transactions.
private static int _backupCounter = 0;
private static void Backup()
{
_backupCounter++;
@ -65,6 +61,8 @@ public static partial class Db
public static Boolean Delete(UserAction actionToDelete)
{
var deleteSuccess = RunTransaction(DeleteAction);
if (deleteSuccess)
Backup();
return deleteSuccess;
@ -73,6 +71,7 @@ public static partial class Db
Boolean DeleteAction()
{
return UserActions.Delete(action => action.Id == actionToDelete.Id) >0;
}
}
@ -86,7 +85,7 @@ public static partial class Db
Boolean DeleteWarning()
{
return Warnings.Delete(error => error.Id == warningToDelete.Id) >0;
return Warnings.Delete(warning => warning.Id == warningToDelete.Id) >0;
}
}
@ -100,15 +99,14 @@ public static partial class Db
Boolean DeleteInstallationAndItsDependencies()
{
InstallationAccess.Delete(i => i.InstallationId == installation.Id);
if (installation.Product == 0)
{
InstallationAccess.Delete(i => i.InstallationId == installation.Id);
//For Salimax, delete the OrderNumber2Installation entries associated with this installation id.
OrderNumber2Installation.Delete(i => i.InstallationId == installation.Id);
}
return Installations.Delete(i => i.Id == installation.Id) > 0;
}
}
@ -123,7 +121,6 @@ public static partial class Db
{
FolderAccess .Delete(u => u.UserId == user.Id);
InstallationAccess.Delete(u => u.UserId == user.Id);
return Users.Delete(u => u.Id == user.Id) > 0;
}
}

View File

@ -1,10 +1,13 @@
using InnovEnergy.App.Backend.DataTypes;
namespace InnovEnergy.App.Backend.Database;
public static partial class Db
{
//We can execute the updates manually for each table, but we prefer the abstract way using Connection.Update method
//We pass an object as an argument and the Connection will connect this object with the corresponding table.
//The update is being done based on the primary id of the object.
private static Boolean Update(Object obj)
{
var success = Connection.Update(obj) > 0;
@ -22,7 +25,6 @@ public static partial class Db
return Update(obj: error);
}
public static Boolean Update(Warning warning)
{
return Update(obj: warning);
@ -44,4 +46,15 @@ public static partial class Db
return Update(obj: user);
}
public static void UpdateAction(UserAction updatedAction)
{
var existingAction = UserActions.FirstOrDefault(action => action.Id == updatedAction.Id);
if (existingAction != null)
{
Update(updatedAction);
}
}
}

View File

@ -26,9 +26,9 @@ public static class Program
var builder = WebApplication.CreateBuilder(args);
RabbitMqManager.InitializeEnvironment();
RabbitMqManager.StartRabbitMqConsumer();
WebsocketManager.MonitorSalimaxInstallationTable();
WebsocketManager.MonitorSalidomoInstallationTable();
RabbitMqManager.StartRabbitMqConsumer().SupressAwaitWarning();
WebsocketManager.MonitorSalimaxInstallationTable().SupressAwaitWarning();
WebsocketManager.MonitorSalidomoInstallationTable().SupressAwaitWarning();
builder.Services.AddControllers();

View File

@ -1,12 +1,10 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.Lib.Utils;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using InnovEnergy.Lib.Mailer;
namespace InnovEnergy.App.Backend.Websockets;
@ -59,23 +57,17 @@ public static class RabbitMqManager
{
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == receivedStatusMessage.Product && f.S3BucketId == receivedStatusMessage.InstallationId);
int installationId = (int)installation.Id;
//if (installationId == 138)
//{
// Console.WriteLine("Received a message from installation: " + installationId + " , product is: " + receivedStatusMessage.Product + " and status is: " + receivedStatusMessage.Status);
//}
//Console.WriteLine("received a message from rabbitmq\n");
//This is a heartbit message, just update the timestamp for this installation.
//There is no need to notify the corresponding front-ends.
//Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue
if (receivedStatusMessage.Type == MessageType.Heartbit)
{
if (installation.Product == 1 && installation.Device == 2)
{
Console.WriteLine("This is a heartbit message from installation: " + installationId + " Name of the file is " + receivedStatusMessage.Timestamp);
}
// if (installation.Product == 1 && installation.Device == 2)
// {
// Console.WriteLine("This is a heartbit message from installation: " + installationId + " Name of the file is " + receivedStatusMessage.Timestamp);
// }
}
else
{
@ -95,7 +87,7 @@ public static class RabbitMqManager
Seen = false
};
//Create a new warning and add it to the database
Console.WriteLine("Add a warning for installation "+installationId);
//Console.WriteLine("Add a warning for installation "+installationId);
Db.HandleWarning(newWarning, installationId);
}
}
@ -176,8 +168,16 @@ public static class RabbitMqManager
prevStatus = WebsocketManager.InstallationConnections[installationId].Status;
WebsocketManager.InstallationConnections[installationId].Status = receivedStatusMessage.Status;
WebsocketManager.InstallationConnections[installationId].Timestamp = DateTime.Now;
// if (installationId == 130)
// {
// Console.WriteLine("prevStatus " + prevStatus + " , new status is: " + receivedStatusMessage.Status + " and status is: " + receivedStatusMessage.Status);
// }
}
installation.Status = receivedStatusMessage.Status;
installation.Apply(Db.Update);
//Console.WriteLine("----------------------------------------------");
//If the status has changed, update all the connected front-ends regarding this installation
if(prevStatus != receivedStatusMessage.Status && WebsocketManager.InstallationConnections[installationId].Connections.Count > 0)

View File

@ -19,32 +19,53 @@ public static class WebsocketManager
{
while (true){
lock (InstallationConnections){
Console.WriteLine("MONITOR SALIMAX INSTALLATIONS\n");
foreach (var installationConnection in InstallationConnections){
if (installationConnection.Value.Product==0 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){
if (installationConnection.Value.Product==0 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)){
Console.WriteLine("Installation ID is "+installationConnection.Key);
Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
installationConnection.Value.Status = -1;
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 0 && f.S3BucketId == installationConnection.Key);
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 0 && f.Id == installationConnection.Key);
installation.Status = -1;
installation.Apply(Db.Update);
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
}
}
Console.WriteLine("FINISHED MONITORING SALIMAX INSTALLATIONS\n");
}
await Task.Delay(TimeSpan.FromMinutes(2));
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
public static async Task MonitorSalidomoInstallationTable()
{
while (true){
Console.WriteLine("TRY TO LOCK FOR MONITOR SALIDOMO INSTALLATIONS\n");
lock (InstallationConnections){
Console.WriteLine("MONITOR SALIDOMO INSTALLATIONS\n");
foreach (var installationConnection in InstallationConnections){
Console.WriteLine("Installation ID is "+installationConnection.Key);
// if (installationConnection.Key == 104)
// {
// Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
// Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
//
//
// }
if (installationConnection.Value.Product==1 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(30))
{
// Console.WriteLine("Installation ID is "+installationConnection.Key);
// Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
// Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 1 && f.S3BucketId == installationConnection.Key);
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 1 && f.Id == installationConnection.Key);
installation.Status = -1;
installation.Apply(Db.Update);
@ -52,8 +73,9 @@ public static class WebsocketManager
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
}
}
Console.WriteLine("FINISHED WITH UPDATING\n");
}
await Task.Delay(TimeSpan.FromMinutes(10));
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
@ -124,7 +146,6 @@ public static class WebsocketManager
//Console.WriteLine("Received a new message from websocket");
lock (InstallationConnections)
{
List<WebsocketMessage> dataToSend = new List<WebsocketMessage>();
//Each front-end will send the list of the installations it wants to access
@ -139,7 +160,7 @@ public static class WebsocketManager
//Console.WriteLine("Create new empty list for installation id " + installationId);
InstallationConnections[installationId] = new InstallationInfo
{
Status = -1,
Status = installation.Status,
Product = installation.Product
};
}
@ -155,15 +176,6 @@ public static class WebsocketManager
dataToSend.Add(jsonObject);
//var jsonString = JsonSerializer.Serialize(jsonObject);
//var dataToSend = Encoding.UTF8.GetBytes(jsonString);
// currentWebSocket.SendAsync(dataToSend,
// WebSocketMessageType.Text,
// true, // Indicates that this is the end of the message
// CancellationToken.None
// );
}
var jsonString = JsonSerializer.Serialize(dataToSend);
var encodedDataToSend = Encoding.UTF8.GetBytes(jsonString);

Binary file not shown.

View File

@ -13,9 +13,9 @@ DEVICE_INSTANCE = 1
SERVICE_NAME_PREFIX = 'com.victronenergy.battery.'
#s3 configuration
S3BUCKET = "673-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXO270612dc3f57a61870220eea"
S3SECRET = "4fPVVN8JGnD9IY1k5RrrNUzo2L1IpR6gdSuGRB9pMWg"
S3BUCKET = "489-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXOd55ec2d5702c3b94f1e275a4"
S3SECRET = "0bOJW6COdJ1_vQ_SDbaYKtLs9E7bxANZb5d1X4zf97g"
# driver configuration

View File

@ -643,6 +643,10 @@ def read_battery_status(modbus, battery):
modbus.connect()
data = read_modbus_registers(modbus, battery.slave_address)
return BatteryStatus(battery, data.registers)
except Exception as e:
logging.error(f"An error occurred: {e}")
create_batch_of_csv_files() # Call this only if there's an error
raise
finally:
modbus.close() # close in any case
@ -655,6 +659,7 @@ def publish_values(dbus, signals, statuses):
previous_warnings = {}
previous_alarms = {}
num_of_csv_files_saved=0
class MessageType:
ALARM_OR_WARNING = "AlarmOrWarning"
@ -680,6 +685,7 @@ def SubscribeToQueue():
connection = pika.BlockingConnection(pika.ConnectionParameters(host="10.2.0.11",
port=5672,
virtual_host="/",
heartbeat=30,
credentials=pika.PlainCredentials("producer", "b187ceaddb54d5485063ddc1d41af66f")))
channel = connection.channel()
channel.queue_declare(queue="statusQueue", durable=True)
@ -903,7 +909,7 @@ def count_files_in_folder(folder_path):
def create_batch_of_csv_files():
global prev_status,INSTALLATION_ID, PRODUCT_ID
global prev_status,INSTALLATION_ID, PRODUCT_ID, num_of_csv_files_saved
# list all files in the directory
files = os.listdir(CSV_DIR)
@ -914,7 +920,8 @@ def create_batch_of_csv_files():
csv_files.sort(key=lambda x: os.path.getctime(os.path.join(CSV_DIR, x)))
# keep the 600 MOST RECENT FILES
recent_csv_files = csv_files[-600:] if len(csv_files) > 600 else csv_files
recent_csv_files = csv_files[-num_of_csv_files_saved:]
print("num_of_csv_files_saved is " + str(num_of_csv_files_saved))
# get the name of the first csv file
if not csv_files:
@ -949,6 +956,7 @@ def create_batch_of_csv_files():
# replace the original first csv file with the temporary file
os.remove(first_csv_file)
os.rename(temp_file_path, first_csv_file)
num_of_csv_files_saved = 0
# create a loggin directory that contains at max 20 batch files for logging info
# logging_dir = os.path.join(CSV_DIR, 'logging_batch_files')
@ -1023,10 +1031,12 @@ def create_update_task(modbus, dbus, batteries, signals, csv_signals, main_loop)
ALLOW = True
alive = update(modbus, batteries, dbus, signals, csv_signals)
elapsed_time = time.time() - start_time
print("11111111111111111111111111111111111111111111 elapsed time is ", elapsed_time)
# keep at most 1900 files at CSV_DIR for logging and aggregation
manage_csv_files(CSV_DIR, 1900)
if elapsed_time >= 1200:
print("CREATE BATCH ======================================>")
create_batch_of_csv_files()
start_time = time.time()
#alive = update_for_testing(modbus, batteries, dbus, signals, csv_signals)
@ -1075,7 +1085,7 @@ def insert_id(path, id_number):
return "/".join(parts)
def create_csv_files(signals, statuses, node_numbers, alarms_number_list, warnings_number_list):
global s3_config
global s3_config, num_of_csv_files_saved
timestamp = int(time.time())
if timestamp % 2 != 0:
timestamp -= 1
@ -1084,6 +1094,8 @@ def create_csv_files(signals, statuses, node_numbers, alarms_number_list, warnin
os.makedirs(CSV_DIR)
csv_filename = f"{timestamp}.csv"
csv_path = os.path.join(CSV_DIR, csv_filename)
num_of_csv_files_saved+=1
# Append values to the CSV file
if not os.path.exists(csv_path):
with open(csv_path, 'a', newline='') as csvfile:

View File

@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3'
# S3 Credentials
S3BUCKET = "140-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXOa947c7fc5990a7a6f6c40860"
S3SECRET = "J1yOTLbYEO6cMxQ2wgIwe__ru9-_RH5BBtKzx_2JJHk"
S3BUCKET = "627-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXOb7bcf7d1e53f2d46923144de"
S3SECRET = "-uUmMuAfx40LpTKTZgdbXswTw09o_qmE4gzkmQS8PTk"

View File

@ -33,6 +33,7 @@ import io
import json
from convert import first
import shutil
CSV_DIR = "/data/csv_files/"
INSTALLATION_NAME_FILE = '/data/innovenergy/openvpn/installation-name'
@ -41,7 +42,6 @@ INSTALLATION_NAME_FILE = '/data/innovenergy/openvpn/installation-name'
if False:
from typing import Callable, List, Iterable, NoReturn
RESET_REGISTER = 0x2087
@ -61,6 +61,7 @@ def compress_csv_data(csv_data, file_name="data.csv"):
return base64_string
class S3config:
def __init__(self):
self.bucket = cfg.S3BUCKET
@ -111,7 +112,9 @@ def SubscribeToQueue():
connection = pika.BlockingConnection(pika.ConnectionParameters(host="10.2.0.11",
port=5672,
virtual_host="/",
credentials=pika.PlainCredentials("producer", "b187ceaddb54d5485063ddc1d41af66f")))
heartbeat=30,
credentials=pika.PlainCredentials("producer",
"b187ceaddb54d5485063ddc1d41af66f")))
channel = connection.channel()
channel.queue_declare(queue="statusQueue", durable=True)
print("Subscribed to queue")
@ -123,10 +126,12 @@ def SubscribeToQueue():
previous_warnings = {}
previous_alarms = {}
class MessageType:
ALARM_OR_WARNING = "AlarmOrWarning"
HEARTBEAT = "Heartbeat"
class AlarmOrWarning:
def __init__(self, description, created_by):
self.date = datetime.now().strftime('%Y-%m-%d')
@ -142,13 +147,16 @@ class AlarmOrWarning:
"CreatedBy": self.created_by
}
channel = SubscribeToQueue()
# Create an S3config instance
s3_config = S3config()
INSTALLATION_ID=int(s3_config.bucket.split('-')[0])
INSTALLATION_ID = int(s3_config.bucket.split('-')[0])
PRODUCT_ID = 1
is_first_update = True
prev_status = 0
num_of_csv_files_saved = 0
def update_state_from_dictionaries(current_warnings, current_alarms, node_numbers):
global previous_warnings, previous_alarms, INSTALLATION_ID, PRODUCT_ID, is_first_update, channel, prev_status
@ -192,17 +200,16 @@ def update_state_from_dictionaries(current_warnings, current_alarms, node_number
for i, alarm_value in enumerate(current_alarms.values()):
if int(list(current_alarms.keys())[i].split("/")[3]) == int(node_number):
if alarm_value:
cnt+=1
cnt += 1
alarms_number_list.append(cnt)
warnings_number_list = []
for node_number in node_numbers:
cnt = 0
for i, warning_value in enumerate(current_warnings.values()):
if int(list(current_warnings.keys())[i].split("/")[3]) == int(node_number):
if warning_value:
cnt+=1
cnt += 1
warnings_number_list.append(cnt)
# Evaluate alarms
@ -221,17 +228,18 @@ def update_state_from_dictionaries(current_warnings, current_alarms, node_number
status_message["Warnings"].append(AlarmOrWarning(description, device_created).to_dict())
if any(current_alarms.values()):
status_message["Status"]=2
status_message["Status"] = 2
if not any(current_alarms.values()) and any(current_warnings.values()):
status_message["Status"]=1
status_message["Status"] = 1
if not any(current_alarms.values()) and not any(current_warnings.values()):
status_message["Status"]=0
status_message["Status"] = 0
if status_message["Status"]!=prev_status or len(status_message["Warnings"])>0 or len(status_message["Alarms"])>0:
prev_status=status_message["Status"]
status_message["Type"]=0
if status_message["Status"] != prev_status or len(status_message["Warnings"]) > 0 or len(
status_message["Alarms"]) > 0:
prev_status = status_message["Status"]
status_message["Type"] = 0
status_message = json.dumps(status_message)
channel.basic_publish(exchange="", routing_key="statusQueue", body=status_message)
print(status_message)
@ -242,6 +250,7 @@ def update_state_from_dictionaries(current_warnings, current_alarms, node_number
return status_message, alarms_number_list, warnings_number_list
def read_csv_as_string(file_path):
"""
Reads a CSV file from the given path and returns its content as a single string.
@ -259,7 +268,6 @@ def read_csv_as_string(file_path):
return None
def init_modbus(tty):
# type: (str) -> Modbus
@ -292,7 +300,6 @@ def report_slave_id(modbus, slave_address):
logging.debug('requesting slave id from node ' + slave)
with modbus:
request = ReportSlaveIdRequest(unit=slave_address)
response = modbus.execute(request)
@ -369,7 +376,6 @@ def read_firmware_version(modbus, slave_address):
logging.debug('reading firmware version')
with modbus:
response = read_modbus_registers(modbus, slave_address, base_address=1054, count=1)
register = response.registers[0]
@ -452,8 +458,8 @@ def upload_status_to_innovenergy(sock, statuses):
def print_usage():
print ('Usage: ' + __file__ + ' <serial device>')
print ('Example: ' + __file__ + ' ttyUSB0')
print('Usage: ' + __file__ + ' <serial device>')
print('Example: ' + __file__ + ' ttyUSB0')
def parse_cmdline_args(argv):
@ -473,7 +479,6 @@ def reset_batteries(modbus, batteries):
logging.info('Resetting batteries...')
for battery in batteries:
result = modbus.write_registers(RESET_REGISTER, [1], unit=battery.slave_address)
# expecting a ModbusIOException (timeout)
@ -490,6 +495,8 @@ def reset_batteries(modbus, batteries):
alive = True # global alive flag, watchdog_task clears it, update_task sets it
start_time = time.time()
def count_files_in_folder(folder_path):
try:
# List all files in the folder
@ -521,8 +528,8 @@ def create_update_task(modbus, service, batteries):
def update_task():
# type: () -> bool
global alive, start_time
try:
global alive, start_time, channel
logging.debug('starting update cycle')
if service.own_properties.get('/ResetBatteries').value == 1:
@ -549,7 +556,7 @@ def create_update_task(modbus, service, batteries):
elapsed_time = time.time() - start_time
create_csv_files(csv_signals, statuses, node_numbers, alarms_number_list, warnings_number_list)
print("11111111111111111111111111111111111111111111 elapsed time is ",elapsed_time)
print("11111111111111111111111111111111111111111111 elapsed time is ", elapsed_time)
# keep at most 1900 files at CSV_DIR for logging and aggregation
manage_csv_files(CSV_DIR, 1900)
@ -562,31 +569,44 @@ def create_update_task(modbus, service, batteries):
upload_status_to_innovenergy(_socket, statuses)
# logging.debug('finished update cycleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n')
# logging.debug('finished update cycleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n')
alive = True
except pika.exceptions.AMQPConnectionError:
logging.error("AMQPConnectionError encountered. Subscribing to queue.")
create_batch_of_csv_files()
except Exception as e:
create_batch_of_csv_files()
logging.error("Unexpected error")
raise
finally:
return True
return update_task
def manage_csv_files(directory_path, max_files=20):
csv_files = [f for f in os.listdir(directory_path) if os.path.isfile(os.path.join(directory_path, f))]
csv_files.sort(key=lambda x: os.path.getctime(os.path.join(directory_path, x)))
print("len of csv files is "+str(len(csv_files)))
print("len of csv files is " + str(len(csv_files)))
# Remove oldest files if exceeds maximum
while len(csv_files) > max_files:
file_to_delete = os.path.join(directory_path, csv_files.pop(0))
os.remove(file_to_delete)
def insert_id(path, id_number):
parts = path.split("/")
insert_position = parts.index("Devices") + 1
parts.insert(insert_position, str(id_number))
return "/".join(parts)
def create_batch_of_csv_files():
global prev_status,channel,INSTALLATION_ID, PRODUCT_ID
global prev_status, channel, INSTALLATION_ID, PRODUCT_ID, num_of_csv_files_saved
# list all files in the directory
files = os.listdir(CSV_DIR)
@ -596,8 +616,9 @@ def create_batch_of_csv_files():
# sort csv files by creation time
csv_files.sort(key=lambda x: os.path.getctime(os.path.join(CSV_DIR, x)))
# keep the 600 MOST RECENT FILES
recent_csv_files = csv_files[-600:] if len(csv_files) > 600 else csv_files
# keep the num_of_csv_files_saved MOST RECENT FILES
recent_csv_files = csv_files[-num_of_csv_files_saved:]
print("num_of_csv_files_saved is " + str(num_of_csv_files_saved))
# get the name of the first csv file
if not csv_files:
@ -629,6 +650,7 @@ def create_batch_of_csv_files():
# replace the original first csv file with the temporary file
os.remove(first_csv_file)
os.rename(temp_file_path, first_csv_file)
num_of_csv_files_saved = 0
# create a loggin directory that contains at max 20 batch files for logging info
# logging_dir = os.path.join(CSV_DIR, 'logging_batch_files')
@ -647,7 +669,6 @@ def create_batch_of_csv_files():
print("error while reading csv as string")
return
# zip-comp additions
compressed_csv = compress_csv_data(csv_data)
# Use the name of the last (most recent) CSV file in sorted csv_files as the name for the compressed file
@ -686,7 +707,6 @@ def create_batch_of_csv_files():
channel = SubscribeToQueue()
channel.basic_publish(exchange="", routing_key="statusQueue", body=status_message)
print("Successfully sent the heartbit with timestamp")
else:
# we save data that were not successfully uploaded in s3 in a failed directory inside the CSV_DIR for logging
@ -698,7 +718,9 @@ def create_batch_of_csv_files():
print("Uploading failed")
manage_csv_files(failed_dir, 100)
def create_csv_files(signals, statuses, node_numbers, alarms_number_list, warnings_number_list):
global num_of_csv_files_saved
timestamp = int(time.time())
if timestamp % 2 != 0:
timestamp -= 1
@ -706,6 +728,7 @@ def create_csv_files(signals, statuses, node_numbers, alarms_number_list, warnin
os.makedirs(CSV_DIR)
csv_filename = "{}.csv".format(timestamp)
csv_path = os.path.join(CSV_DIR, csv_filename)
num_of_csv_files_saved += 1
if not os.path.exists(csv_path):
with open(csv_path, 'ab') as csvfile:
@ -715,14 +738,15 @@ def create_csv_files(signals, statuses, node_numbers, alarms_number_list, warnin
config_row = [nodes_config_path, nodes_list, ""]
csv_writer.writerow(config_row)
for i, node in enumerate(node_numbers):
csv_writer.writerow(["/Battery/Devices/{}/Alarms".format(str(i+1)), alarms_number_list[i], ""])
csv_writer.writerow(["/Battery/Devices/{}/Warnings".format(str(i+1)), warnings_number_list[i], ""])
csv_writer.writerow(["/Battery/Devices/{}/Alarms".format(str(i + 1)), alarms_number_list[i], ""])
csv_writer.writerow(["/Battery/Devices/{}/Warnings".format(str(i + 1)), warnings_number_list[i], ""])
for s in signals:
signal_name = insert_id(s.name, i+1)
signal_name = insert_id(s.name, i + 1)
value = s.get_value(statuses[i])
row_values = [signal_name, value, s.get_text]
csv_writer.writerow(row_values)
def create_watchdog_task(main_loop):
# type: (DBusGMainLoop) -> Callable[[],bool]
"""
@ -730,6 +754,7 @@ def create_watchdog_task(main_loop):
The watchdog kills the main loop if the alive flag is not periodically reset by the update task.
Who watches the watchdog?
"""
def watchdog_task():
# type: () -> bool
@ -746,7 +771,10 @@ def create_watchdog_task(main_loop):
return watchdog_task
BATTERY_COUNTS_FILE = '/data/battery_count.csv'
def load_battery_counts():
if os.path.exists(BATTERY_COUNTS_FILE):
with open(BATTERY_COUNTS_FILE, 'r') as f:
@ -760,6 +788,8 @@ def save_battery_counts(battery_counts):
writer = csv.writer(f)
for count in battery_counts:
writer.writerow([count])
def main(argv):
# type: (List[str]) -> ()
print("INSIDE DBUS SONICK")
@ -780,11 +810,11 @@ def main(argv):
retry_attempts = 0
while True:
modbus = init_modbus(tty)
batteries = identify_batteries(modbus)
n = len(batteries)
logging.info('found %d %s', n, "battery" if n == 1 else "batteries")
if n <= 0:
sys.exit(2) # Exit if no batteries are found
@ -805,7 +835,6 @@ def main(argv):
logging.warning('Max retry attempts reached. Continuing with fewer batteries.')
save_battery_counts(battery_counts)
break
continue
elif n == max(battery_counts):
@ -825,8 +854,9 @@ def main(argv):
update_task() # run it right away, so that all props are initialized before anyone can ask
watchdog_task = create_watchdog_task(main_loop)
gobject.timeout_add(cfg.UPDATE_INTERVAL * 2, watchdog_task, priority = gobject.PRIORITY_LOW) # add watchdog first
gobject.timeout_add(cfg.UPDATE_INTERVAL, update_task, priority = gobject.PRIORITY_LOW) # call update once every update_interval
gobject.timeout_add(cfg.UPDATE_INTERVAL * 2, watchdog_task, priority=gobject.PRIORITY_LOW) # add watchdog first
gobject.timeout_add(cfg.UPDATE_INTERVAL, update_task,
priority=gobject.PRIORITY_LOW) # call update once every update_interval
logging.info('starting gobject.MainLoop')
main_loop.run()
@ -836,4 +866,3 @@ def main(argv):
main(sys.argv[1:])

View File

@ -141,7 +141,7 @@ async def main(remote_host):
##### 2. check whether it's Venus ######
gx_type = await check_GX_type(remote_host)
if gx_type == "beaglebone\n":
# if gx_type == "beaglebone\n":
##### 3. upload VPN and battery files ######
print("Upload pika and battery files!")
if(await upload_files(remote_host)!="All files uploaded successfully."):
@ -172,8 +172,8 @@ async def main(remote_host):
##### 11. restart gui ######
print("Restart gui!")
print(await restart_gui(remote_host))
else:
sys.exit("It's not Venus GX!")
# else:
# sys.exit("It's not Venus GX!")
if __name__ == "__main__":

View File

@ -1,11 +1,11 @@
{
"name": "Inesco Energy",
"name": "InnovEnergy",
"version": "2.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "Inesco Energy",
"name": "InnovEnergy",
"version": "2.0.0",
"dependencies": {
"@emotion/react": "11.9.0",

View File

@ -1,7 +1,7 @@
{
"name": "c",
"name": "InnovEnergy",
"version": "2.0.0",
"title": "Inesco Energy",
"title": "InnovEnergy",
"private": false,
"dependencies": {
"@emotion/react": "11.9.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@ -13,7 +13,7 @@
href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400&display=swap"
rel="stylesheet"
/>
<title>Inesco Energy</title>
<title>InnovEnergy</title>
</head>
<body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 841.89 595.28" style="enable-background:new 0 0 841.89 595.28;" xml:space="preserve">
<style type="text/css">
.st0{fill:#646363;}
.st1{fill:#F39200;}
</style>
<g>
<path class="st0" d="M114.05,430.69c2.55,1.07,6.32,1.94,10.5,1.94c6.47,0,12.13-2.7,12.13-9.69c0-7.03-4.99-9.02-11.06-11.01
c-4.79-1.53-7.55-2.91-7.55-7.04c0-3.82,2.55-5.91,7.9-5.91c2.45,0,5.15,0.61,7.54,1.38l0.76-3.11c-2.24-0.87-5.71-1.53-8.56-1.53
c-6.98,0-11.42,3.21-11.42,9.23c0,5.71,3.52,8.21,9.53,10.14c5.45,1.68,9.07,3.11,9.07,7.85c0,4.38-3.26,6.42-8.46,6.42
c-3.98,0-7.29-1.07-9.68-1.94L114.05,430.69L114.05,430.69z M165.59,418.56c0-7.54-3.98-12.85-11.98-12.85
c-2.86,0-6.42,0.71-9.64,2.39v33.34h3.57v-10.14c1.38,0.66,3.26,1.27,5.86,1.27C161.76,432.58,165.59,426.72,165.59,418.56
L165.59,418.56z M147.54,428.09V410.3c1.53-0.87,3.57-1.48,5.91-1.48c5.91,0,8.41,4.38,8.41,9.79c0,6.98-2.96,10.91-8.82,10.91
C150.6,429.52,148.92,428.81,147.54,428.09L147.54,428.09z M192.96,427.84c-2.4,1.02-5.1,1.68-7.7,1.68
c-5.81,0-9.48-3.11-9.79-9.63h18.35c0.1-0.92,0.15-1.89,0.15-3.01c0-6.22-3.62-11.16-10.4-11.16c-7.34,0-11.77,5.61-11.77,13.41
c0,8.77,5.2,13.51,13.2,13.51c3.21,0,6.12-0.61,8.61-1.78L192.96,427.84L192.96,427.84z M183.58,408.77c4.89,0,6.88,3.72,6.88,7.6
c0,0.41,0,0.61-0.05,0.97h-14.88C175.99,412.24,178.79,408.77,183.58,408.77L183.58,408.77z M205.55,406.33h-3.57v25.69h3.57
V406.33L205.55,406.33z M203.77,400.98c1.53,0,2.4-1.07,2.4-2.45c0-1.33-0.87-2.35-2.4-2.35c-1.53,0-2.4,1.02-2.4,2.35
C201.37,399.9,202.24,400.98,203.77,400.98L203.77,400.98z M225.89,405.72c-7.85,0-12.29,5.51-12.29,13.66
c0,8.41,4.64,13.25,12.9,13.25c2.55,0,5.15-0.66,6.98-1.63l-0.71-2.96c-1.89,0.92-4.03,1.48-6.12,1.48
c-6.17,0-9.38-3.57-9.38-10.19c0-6.37,3.01-10.5,8.82-10.5c2.14,0,4.43,0.56,6.37,1.32l0.76-2.9
C231.4,406.38,228.8,405.72,225.89,405.72L225.89,405.72z M244.24,432.02v-21.26c2.04-1.22,4.38-1.94,7.14-1.94
c4.54,0,6.73,2.5,6.73,6.52v16.67h3.57V415.1c0-5.81-3.57-9.38-9.94-9.38c-3.21,0-5.45,0.82-7.49,1.89v-12.49l-3.57,0.25v36.65
H244.24L244.24,432.02z M290.42,427.84c-2.4,1.02-5.1,1.68-7.7,1.68c-5.81,0-9.48-3.11-9.79-9.63h18.35
c0.1-0.92,0.15-1.89,0.15-3.01c0-6.22-3.62-11.16-10.4-11.16c-7.34,0-11.78,5.61-11.78,13.41c0,8.77,5.2,13.51,13.2,13.51
c3.21,0,6.12-0.61,8.61-1.78L290.42,427.84L290.42,427.84z M281.05,408.77c4.89,0,6.88,3.72,6.88,7.6c0,0.41,0,0.61-0.05,0.97
h-14.88C273.45,412.24,276.25,408.77,281.05,408.77L281.05,408.77z M299.45,408.32v23.7h3.57v-21.46c1.78-1.02,4.23-1.73,7.34-1.73
c0.61,0,1.48,0.05,2.14,0.25l0.51-3.11c-0.87-0.15-1.73-0.25-2.85-0.25C306.02,405.72,302.15,406.79,299.45,408.32L299.45,408.32z
M322.44,432.02v-21.46c2.19-1.17,4.69-1.73,7.24-1.73c4.59,0,6.63,2.55,6.63,6.47v16.72h3.57V415.1c0-5.81-3.31-9.38-10.2-9.38
c-3.98,0-7.7,1.02-10.81,2.6v23.7H322.44L322.44,432.02z M402.21,432.02V415.1c0-5.56-3.11-9.38-9.89-9.38
c-3.62,0-6.63,1.17-9.12,2.65c-1.63-1.68-4.03-2.65-7.29-2.65c-3.82,0-7.39,1.02-10.45,2.6v23.7h3.57v-21.46
c2.09-1.17,4.43-1.73,6.78-1.73c4.64,0,6.27,2.75,6.27,6.47v16.72h3.52v-16.46c0-1.68-0.26-3.16-0.71-4.49
c1.89-1.27,4.64-2.24,7.34-2.24c4.59,0,6.42,2.75,6.42,6.47v16.72H402.21L402.21,432.02z M415.21,406.33h-3.57v25.69h3.57V406.33
L415.21,406.33z M413.42,400.98c1.53,0,2.4-1.07,2.4-2.45c0-1.33-0.87-2.35-2.4-2.35c-1.53,0-2.4,1.02-2.4,2.35
C411.03,399.9,411.89,400.98,413.42,400.98L413.42,400.98z M436.67,432.12l-0.26-3.01c-0.87,0.25-2.09,0.41-3.11,0.41
c-2.75,0-4.38-1.27-4.38-5.05v-15.14h6.93v-3.01h-6.93v-6.32l-3.57,0.26v6.07h-3.87v3.01h3.87v15.14c0,5.96,3.01,8.16,7.49,8.16
C434.32,432.63,435.49,432.43,436.67,432.12L436.67,432.12z M457.31,430.69c2.55,1.07,6.32,1.94,10.5,1.94
c6.47,0,12.13-2.7,12.13-9.69c0-7.03-5-9.02-11.06-11.01c-4.79-1.53-7.54-2.91-7.54-7.04c0-3.82,2.55-5.91,7.9-5.91
c2.45,0,5.15,0.61,7.54,1.38l0.76-3.11c-2.24-0.87-5.71-1.53-8.56-1.53c-6.98,0-11.42,3.21-11.42,9.23c0,5.71,3.52,8.21,9.53,10.14
c5.45,1.68,9.07,3.11,9.07,7.85c0,4.38-3.26,6.42-8.46,6.42c-3.98,0-7.29-1.07-9.69-1.94L457.31,430.69L457.31,430.69z
M495.28,429.78c-3.98,0-6.37-1.58-6.37-4.79c0-3.82,2.85-5.1,6.68-5.1c2.29,0,4.13,0.36,5.81,0.71v7.75
C500.28,429.06,498.34,429.78,495.28,429.78L495.28,429.78z M504.92,430.24V415.2c0-6.12-3.31-9.48-9.94-9.48
c-2.9,0-5.96,0.61-7.95,1.48l0.82,2.96c1.89-0.81,4.38-1.32,6.88-1.32c4.64,0,6.68,2.29,6.68,6.22V418
c-1.84-0.41-4.08-0.71-6.17-0.71c-5.45,0.05-9.89,2.29-9.89,7.8c0,4.43,3.31,7.55,9.63,7.55
C499.51,432.63,502.67,431.56,504.92,430.24L504.92,430.24z M517.66,395.11l-3.57,0.25v29.41c0,5.15,2.09,7.85,6.47,7.85
c0.71,0,1.63-0.15,2.29-0.31l-0.31-2.91c-0.36,0.05-0.87,0.15-1.48,0.15c-2.6,0-3.41-1.63-3.41-4.84V395.11L517.66,395.11z
M527.4,406.33v3.01h14.53c-3.62,6.47-9.18,13.41-15.39,20.14v2.55h20.14v-3.01h-15.65c6.07-6.63,11.01-13.15,14.89-20.14v-2.55
H527.4L527.4,406.33z M558.08,396.34h-4.03v12.13c0,3.31,0.2,7.39,0.61,11.98h2.8c0.41-4.64,0.61-8.61,0.61-11.93V396.34
L558.08,396.34z M558.44,430.03c0-1.33-0.87-2.29-2.29-2.29c-1.53,0-2.4,0.97-2.4,2.29s0.87,2.35,2.4,2.35
C557.57,432.38,558.44,431.36,558.44,430.03L558.44,430.03z"/>
<path class="st0" d="M305.72,269.82v-65.28c5.44-2.72,12-4.16,18.72-4.16c12.16,0,18.08,6.72,18.08,18.4v51.04h16.16v-52.16
c0-19.52-11.84-30.88-34.24-30.88c-12.32,0-24.96,2.88-34.88,7.52v75.52H305.72L305.72,269.82z M399,269.82v-65.28
c5.44-2.72,12-4.16,18.72-4.16c12.16,0,18.08,6.72,18.08,18.4v51.04h16.16v-52.16c0-19.52-11.84-30.88-34.24-30.88
c-12.32,0-24.96,2.88-34.88,7.52v75.52H399L399,269.82z M547.32,229.18c0-25.28-14.08-42.4-38.08-42.4
c-23.68,0-37.92,17.12-37.92,42.4c0,25.44,14.24,42.56,37.92,42.56C533.24,271.74,547.32,254.62,547.32,229.18L547.32,229.18z
M487.8,229.18c0-17.44,7.36-28.8,21.44-28.8c14.56,0,21.44,11.36,21.44,28.8c0,17.6-6.88,28.96-21.44,28.96
C495.16,258.14,487.8,246.78,487.8,229.18L487.8,229.18z M575.8,188.7h-16.32c3.2,31.2,11.84,53.12,28.16,81.12h17.92
c16-28,24.32-49.92,28.16-81.12h-16.16c-2.56,27.36-9.92,47.2-20.96,67.68C585.88,235.9,578.68,216.06,575.8,188.7L575.8,188.7z"/>
<path class="st1" d="M307.68,360.51c-7.2,3.04-15.36,4.96-23.36,4.96c-15.68,0-25.44-7.84-27.04-24.96h54.56
c0.32-3.04,0.64-6.56,0.64-10.72c0-20.32-12.48-35.52-33.6-35.52c-23.36,0-38.24,17.44-38.24,42.4c0,27.04,16.8,42.56,42.24,42.56
c10.4,0,20-2.08,27.52-5.6L307.68,360.51L307.68,360.51z M278.88,307.55c12.32,0,17.92,9.28,17.92,20c0,0.96,0,1.6-0.16,2.56h-39.2
C258.88,316.35,266.4,307.55,278.88,307.55L278.88,307.55z M348.07,377.31v-65.28c5.44-2.72,12-4.16,18.72-4.16
c12.16,0,18.08,6.72,18.08,18.4v51.04h16.16v-52.16c0-19.52-11.84-30.88-34.24-30.88c-12.32,0-24.96,2.88-34.88,7.52v75.52H348.07
L348.07,377.31z M487.43,360.51c-7.2,3.04-15.36,4.96-23.36,4.96c-15.68,0-25.44-7.84-27.04-24.96h54.56
c0.32-3.04,0.64-6.56,0.64-10.72c0-20.32-12.48-35.52-33.6-35.52c-23.36,0-38.24,17.44-38.24,42.4c0,27.04,16.8,42.56,42.24,42.56
c10.4,0,20-2.08,27.52-5.6L487.43,360.51L487.43,360.51z M458.63,307.55c12.32,0,17.92,9.28,17.92,20c0,0.96,0,1.6-0.16,2.56h-39.2
C438.63,316.35,446.15,307.55,458.63,307.55L458.63,307.55z M512.71,301.95v75.36h16.16v-65.28c4.48-2.4,10.56-4.16,18.56-4.16
c2.56,0,5.6,0.32,8,1.12l2.24-13.6c-2.88-0.64-6.88-1.12-11.36-1.12C533.99,294.27,521.03,297.31,512.71,301.95L512.71,301.95z
M636.05,375.07v-74.4c-7.04-3.36-17.92-6.4-29.92-6.4c-24.8,0-41.12,15.84-41.12,41.44c0,24.32,14.4,38.24,37.28,38.24
c6.72,0,12.64-1.6,17.6-4.16v6.72c0,11.04-7.52,17.6-21.76,17.6c-8.48,0-15.36-1.28-22.4-4l-3.36,13.12
c8.16,3.2,15.2,4.64,26.72,4.64C621.81,407.87,636.05,396.35,636.05,375.07L636.05,375.07z M581.49,334.43
c0-16.48,9.44-26.56,24.64-26.56c5.12,0,10.24,1.28,13.76,2.56v45.6c-4,2.56-8.96,4.16-15.36,4.16
C589.97,360.19,581.49,350.43,581.49,334.43L581.49,334.43z M700.69,372.83c12.8-22.88,22.56-47.68,25.76-76.64h-16.16
c-2.4,27.36-8.48,45.12-18.56,65.28c-11.04-18.56-19.36-37.92-21.92-65.28h-16.32c3.36,30.72,12.64,53.44,29.76,77.76
c-6.24,10.4-16.96,16.96-27.36,19.04l2.72,13.92C674.45,403.71,688.53,394.11,700.69,372.83L700.69,372.83z"/>
<polygon class="st0" points="263.6,188.59 247.44,188.59 247.44,269.71 263.6,269.71 263.6,188.59 "/>
<path class="st1" d="M255.44,174.51c6.24,0,9.92-4,9.92-9.92c0-5.6-3.68-9.6-9.92-9.6c-6.4,0-10.08,4-10.08,9.6
C245.36,170.51,249.04,174.51,255.44,174.51L255.44,174.51z"/>
<path class="st0" d="M666.38,255.96c0,8.32-4.87,12.94-12.92,12.94c-8.12,0-12.85-4.62-12.85-12.94c0-8.25,4.73-12.94,12.85-12.94
C661.51,243.03,666.38,247.71,666.38,255.96L666.38,255.96z M637.36,255.96c0,9.51,6.29,15.78,16.1,15.78
c9.61,0,16.17-6.27,16.17-15.78s-6.49-15.78-16.17-15.78C643.72,240.19,637.36,246.46,637.36,255.96L637.36,255.96z M647.78,263.69
h3.11v-5.81h2.37c1.02,1.98,2.23,3.96,3.79,5.81h3.79c-1.83-2.18-3.31-4.23-4.4-6.27c2.23-0.73,3.25-2.38,3.25-4.62
c0-3.1-2.03-5.15-6.49-5.15h-5.41V263.69L647.78,263.69z M653.19,250.15c2.3,0,3.52,0.79,3.52,2.64c0,1.85-1.22,2.64-3.52,2.64
h-2.3v-5.28H653.19L653.19,250.15z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -18,7 +18,7 @@ function Footer() {
>
<Box>
<Typography variant="subtitle1">
&copy; 2024 - Inesco Energy Solutions AG
&copy; 2024 - InnovEnergy AG
</Typography>
</Box>
<Typography
@ -33,7 +33,7 @@ function Footer() {
target="_blank"
rel="noopener noreferrer"
>
Inesco Energy Solutions AG
InnovEnergy AG
</Link>
</Typography>
</Box>

View File

@ -3,11 +3,14 @@ import {
Box,
Button,
CircularProgress,
Container,
Grid,
Modal,
TextField,
Typography,
useTheme
} from '@mui/material';
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
import { UserContext } from 'src/contexts/userContext';
import { TokenContext } from 'src/contexts/tokenContext';
import Avatar from '@mui/material/Avatar';
@ -15,7 +18,6 @@ import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import axiosConfig from 'src/Resources/axiosConfig';
import { useNavigate } from 'react-router-dom';
import routes from 'src/Resources/routes.json';
import inescologo from '../Resources/images/Logo.svg';
interface ForgotPasswordPromps {
resetPassword: () => void;
@ -71,6 +73,16 @@ function ForgotPassword() {
return (
<>
<Container maxWidth="xl" sx={{ pt: 2 }}>
<Grid container>
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
<a href="https://monitor.innov.energy/">
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
</a>
</Grid>
</Grid>
</Container>
<Box
sx={{
marginTop: 8,
@ -88,12 +100,7 @@ function ForgotPassword() {
transform: 'translate(-50%, -50%)'
}}
>
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
<a href="https://monitor.innov.energy/">
<img src={inescologo} alt="inescologo" height="100" />
</a>
</Box>
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
@ -127,15 +134,15 @@ function ForgotPassword() {
}}
/>
{loading && <CircularProgress sx={{ color: '#00b33c' }} />}
{loading && <CircularProgress sx={{ color: '#ffc04d' }} />}
<Button
sx={{
mt: 3,
mb: 2,
textTransform: 'none',
bgcolor: '#00b33c',
'&:hover': { bgcolor: '#009933' }
bgcolor: '#ffc04d',
'&:hover': { bgcolor: '#f7b34d' }
}}
variant="contained"
fullWidth={true}
@ -174,9 +181,9 @@ function ForgotPassword() {
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#00b33c',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#009933' }
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={() => setErrorModalOpen(false)}
>
@ -214,9 +221,9 @@ function ForgotPassword() {
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#00b33c',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#009933' }
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleReturn}
>

View File

@ -3,18 +3,20 @@ import {
Box,
Button,
CircularProgress,
Container,
Grid,
Modal,
TextField,
Typography,
useTheme
} from '@mui/material';
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
import axiosConfig from 'src/Resources/axiosConfig';
import { UserContext } from 'src/contexts/userContext';
import { TokenContext } from 'src/contexts/tokenContext';
import { useNavigate } from 'react-router-dom';
import Avatar from '@mui/material/Avatar';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import inescologo from '../Resources/images/Logo.svg';
function ResetPassword() {
const [username, setUsername] = useState('');
@ -68,6 +70,16 @@ function ResetPassword() {
return (
<>
<Container maxWidth="xl" sx={{ pt: 2 }}>
<Grid container>
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
<a href="https://monitor.innov.energy/">
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
</a>
</Grid>
</Grid>
</Container>
<Box
sx={{
marginTop: 8,
@ -85,12 +97,7 @@ function ResetPassword() {
transform: 'translate(-50%, -50%)'
}}
>
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
<a href="https://monitor.innov.energy/">
<img src={inescologo} alt="inescologo" height="100" />
</a>
</Box>
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
@ -130,7 +137,7 @@ function ResetPassword() {
/>
{loading && (
<CircularProgress sx={{ color: '#00b33c', marginLeft: '170px' }} />
<CircularProgress sx={{ color: '#ffc04d', marginLeft: '170px' }} />
)}
{password != verifypassword && (
@ -148,8 +155,8 @@ function ResetPassword() {
mt: 3,
mb: 2,
textTransform: 'none',
bgcolor: '#00b33c',
'&:hover': { bgcolor: '#009933' }
bgcolor: '#ffc04d',
'&:hover': { bgcolor: '#f7b34d' }
}}
variant="contained"
fullWidth={true}
@ -188,9 +195,9 @@ function ResetPassword() {
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#00b33c',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#009933' }
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={() => setOpen(false)}
>

View File

@ -3,18 +3,20 @@ import {
Box,
Button,
CircularProgress,
Container,
Grid,
Modal,
TextField,
Typography,
useTheme
} from '@mui/material';
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
import axiosConfig from 'src/Resources/axiosConfig';
import { UserContext } from 'src/contexts/userContext';
import { TokenContext } from 'src/contexts/tokenContext';
import { useNavigate } from 'react-router-dom';
import Avatar from '@mui/material/Avatar';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import inescologo from '../Resources/images/Logo.svg';
function SetNewPassword() {
const [username, setUsername] = useState('');
@ -69,6 +71,16 @@ function SetNewPassword() {
return (
<>
<Container maxWidth="xl" sx={{ pt: 2 }}>
<Grid container>
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
<a href="https://monitor.innov.energy/">
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
</a>
</Grid>
</Grid>
</Container>
<Box
sx={{
marginTop: 8,
@ -86,12 +98,7 @@ function SetNewPassword() {
transform: 'translate(-50%, -50%)'
}}
>
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
<a href="https://monitor.innov.energy/">
<img src={inescologo} alt="inescologo" height="100" />
</a>
</Box>
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
@ -131,7 +138,7 @@ function SetNewPassword() {
/>
{loading && (
<CircularProgress sx={{ color: '#00b33c', marginLeft: '0px' }} />
<CircularProgress sx={{ color: '#ffc04d', marginLeft: '0px' }} />
)}
{password != verifypassword && (
@ -149,8 +156,8 @@ function SetNewPassword() {
mt: 3,
mb: 2,
textTransform: 'none',
bgcolor: '#00b33c',
'&:hover': { bgcolor: '#009933' }
bgcolor: '#ffc04d',
'&:hover': { bgcolor: '#f7b34d' }
}}
variant="contained"
fullWidth={true}
@ -189,9 +196,9 @@ function SetNewPassword() {
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#00b33c',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#009933' }
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={() => setOpen(false)}
>

View File

@ -4,6 +4,7 @@ import {
Button,
Checkbox,
CircularProgress,
Container,
FormControlLabel,
Grid,
Modal,
@ -14,7 +15,7 @@ import {
import Avatar from '@mui/material/Avatar';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import Link from '@mui/material/Link';
import inescologo from 'src/Resources/images/Logo.svg';
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
import { axiosConfigWithoutToken } from 'src/Resources/axiosConfig';
import Cookies from 'universal-cookie';
import { UserContext } from 'src/contexts/userContext';
@ -101,6 +102,16 @@ function Login() {
return (
<div style={{ userSelect: 'none' }}>
<Container maxWidth="xl" sx={{ pt: 2 }} className="login">
<Grid container>
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
<a href="https://monitor.innov.energy/">
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
</a>
</Grid>
</Grid>
</Container>
<Box
sx={{
marginTop: 8,
@ -118,12 +129,7 @@ function Login() {
transform: 'translate(-50%, -50%)'
}}
>
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
<a href="https://monitor.innov.energy/">
<img src={inescologo} alt="inescologo" height="100" />
</a>
</Box>
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
@ -179,7 +185,7 @@ function Login() {
checked={rememberMe}
onChange={handleRememberMeChange}
icon={<CheckBoxOutlineBlankIcon style={{ color: 'grey' }} />}
checkedIcon={<CheckBoxIcon style={{ color: '#00b33c' }} />}
checkedIcon={<CheckBoxIcon style={{ color: '#ffc04d' }} />}
style={{ marginLeft: -175 }}
/>
}
@ -190,8 +196,8 @@ function Login() {
sx={{
mb: 2,
textTransform: 'none',
bgcolor: '#00b33c',
'&:hover': { bgcolor: '#009933' }
bgcolor: '#ffc04d',
'&:hover': { bgcolor: '#f7b34d' }
}}
variant="contained"
fullWidth={true}
@ -204,7 +210,7 @@ function Login() {
{loading && (
<CircularProgress
sx={{
color: '#009933',
color: '#ffc04d',
marginLeft: '20px'
}}
/>
@ -239,9 +245,9 @@ function Login() {
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#00b33c',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#009933' }
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={() => setOpen(false)}
>

View File

@ -390,11 +390,7 @@ function InstallationTabs() {
return salimaxInstallations.length > 1 ? (
<>
<Container
maxWidth="xl"
sx={{ marginLeft: '40px', marginTop: '20px' }}
className="mainframe"
>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper>
<Tabs
onChange={handleTabsChange}
@ -456,11 +452,7 @@ function InstallationTabs() {
</>
) : salimaxInstallations.length === 1 ? (
<>
<Container
maxWidth="xl"
sx={{ marginLeft: '40px', marginTop: '20px' }}
className="mainframe"
>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper>
<Tabs
onChange={handleTabsChange}

View File

@ -129,7 +129,12 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
</TableRow>
</TableHead>
<TableBody>
{sortedInstallations.map((installation) => {
{sortedInstallations
.filter(
(installation) =>
installation.status === -1 && installation.device === 1
)
.map((installation) => {
const isInstallationSelected =
installation.s3BucketId === selectedInstallation;

View File

@ -276,11 +276,7 @@ function SalidomoInstallationTabs() {
return salidomoInstallations.length > 1 ? (
<>
<Container
maxWidth="xl"
sx={{ marginLeft: '40px', marginTop: '20px' }}
className="mainframe"
>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper>
<Tabs
onChange={handleTabsChange}
@ -346,11 +342,7 @@ function SalidomoInstallationTabs() {
) : salidomoInstallations.length === 1 ? (
<>
{' '}
<Container
maxWidth="xl"
sx={{ marginLeft: '40px', marginTop: '20px' }}
className="mainframe"
>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper>
<Tabs
onChange={handleTabsChange}

View File

@ -1,6 +1,7 @@
import { useContext } from 'react';
import Scrollbar from 'src/components/Scrollbar';
import { SidebarContext } from 'src/contexts/SidebarContext';
import innovenergyLogo from 'src/Resources/images/innovenergy-Logo_Speichern-mit-Salz_R_color.svg';
import {
alpha,
Box,
@ -13,11 +14,10 @@ import {
} from '@mui/material';
import SidebarMenu from './SidebarMenu';
import Logo from 'src/Resources/images/Logo_for_dark_bg.svg';
const SidebarWrapper = styled(Box)(
({ theme }) => `
width : 250px;
width: ${theme.sidebar.width};
min-width: ${theme.sidebar.width};
color: ${theme.colors.alpha.trueWhite[70]};
position: relative;
@ -60,11 +60,10 @@ function Sidebar() {
}}
>
<img
src={Logo}
alt="inesco logo"
src={innovenergyLogo}
alt="innovenergy logo"
style={{
marginLeft: '30px',
width: '150px'
width: '150px' // Width of the image
}}
/>
</Box>
@ -106,8 +105,8 @@ function Sidebar() {
}}
>
<img
src={Logo}
alt="innesco logo"
src={innovenergyLogo}
alt="innovenergy logo"
style={{
width: '150px' // Width of the image
}}