Merge remote-tracking branch 'origin/main'

This commit is contained in:
atef 2025-06-11 15:53:26 +02:00
commit ab051a88d1
14 changed files with 3360 additions and 70 deletions

View File

@ -0,0 +1,90 @@
This README file provides a comprehensive guide to utilizing a Python script for interacting with S3 storage,
specifically designed for downloading and processing data files based on a specified time range and key parameters.
The script requires Python3 installed on your system and makes use of the s3cmd tool for accessing data in cloud storage.
It also illustrates the process of configuring s3cmd by creating a .s3cfg file with your access credentials. Nice
############ Create the .s3cfg file in home directory ################
nano .s3cfg
Copy this lines inside the file.
[default]
host_base = sos-ch-dk-2.exo.io
host_bucket = %(bucket)s.sos-ch-dk-2.exo.io
access_key = EXO4d838d1360ba9fb7d51648b0
secret_key = _bmrp6ewWAvNwdAQoeJuC-9y02Lsx7NV6zD-WjljzCU
use_https = True
############ S3cmd instalation ################
Please install s3cmd for retrieving data from our Cloud storage.
sudo apt install s3cmd
############ Python3 instalation ################
To check if you have already have python3, run this command
python3 --version
To install you can use this command:
1) sudo apt update
2) sudo apt install python3
3) python3 --version (to check if pyhton3 installed correctly)
############ Run extractS3data.py ################
usage: extractRange.py [-h] --key KEY --bucket-number BUCKET_NUMBER start_timestamp end_timestamp
KEY: the key can be a one word or a path
for example: /DcDc/Devices/2/Status/Dc/Battery/voltage ==> this will provide us a Dc battery Voltage of the DcDc device 2.
example : Dc/Battery/voltage ==> This will provide all DcDc Device voltage (including the avg voltage of all DcDc device)
example : voltage ==> This will provide all voltage of all devices in the Salimax
BUCKET_NUMBER: This a number of bucket name for the instalation
start_timestamp end_timestamp: this must be a correct timestamp of 10 digits.
The start_timestamp must be smaller than the end_timestamp.
PS: The data will be downloaded to a folder named S3cmdData_{Bucket_Number}. If this folder does not exist, it will be created.
If the folder exist, it will try to download data between the requested timestamps if they files are not already existing.
Example command:
python3 extractS3data.py 1749062721 1749106001 --keys GridMeter/Ac/Power/Active --bucket-number 12 --product_name=SodistoreMax
################################ EXTENDED FEATURES FOR MORE ADVANCED USAGE ################################
1) Multiple Keys Support:
The script supports the extraction of data using multiple keys. Users can specify one or multiple keys separated by commas with the --keys parameter.
This feature allows for more granular data extraction, catering to diverse data analysis requirements. For example, users can extract data for different
metrics or parameters from the same or different CSV files within the specified range.
3) Dynamic Header Generation:
The script dynamically generates headers for the output CSV file based on the keys provided. This ensures that the output file accurately reflects the
extracted data, providing a clear and understandable format for subsequent analysis. The headers correspond to the keys used for data extraction, making
it easy to identify and analyze the extracted data.
4)Advanced Data Processing Capabilities:
Booleans as Numbers: The --booleans_as_numbers flag allows users to convert boolean values (True/False) into numeric representations (1/0). This feature
is particularly useful for analytical tasks that require numerical data processing.
Example Command:
python3 extractS3data.py 1749062721 1749106001 --keys AcDc/SystemControl/ResetAlarmsAndWarnings,AcDc/Devices/1/Status/Ac/L1/Voltage --bucket-number 12 --product_name=SodistoreMax
This command extracts data for AcDc/SystemControl/ResetAlarmsAndWarnings and AcDc/Devices/1/Status/Ac/L1/Voltage keys from bucket number 12, between the specified timestamps, with boolean values converted to numbers.

View File

@ -0,0 +1,937 @@
{
"AcDc": {
"SystemControl": {
"Alarms": "",
"CommunicationTimeout": "00:00:20",
"SystemConfig": "AcDcAndDcDc",
"ResetAlarmsAndWarnings": "True",
"TargetSlave": 0.0,
"UseSlaveIdForAddressing": "True",
"ReferenceFrame": "Consumer",
"SlaveErrorHandling": "Relaxed",
"SubSlaveErrorHandling": "Off",
"PowerSetPointActivation": "Immediate",
"PowerSetPointTrigger": "Wait",
"DeviceState": "Operation",
"NumberOfConnectedSlaves": 2.0,
"NumberOfConnectedSubSlaves": 0.0,
"Warnings": ""
},
"Devices": {
"1": {
"Status": {
"Ac": {
"L1": {
"Voltage": 238.8,
"Current": 1.45,
"Phi": 3.09,
"Power": {
"Active": -345.73,
"Reactive": 19.21,
"Apparent": 346.26
}
},
"L2": {
"Voltage": 239.2,
"Current": 1.53,
"Phi": -3.09,
"Power": {
"Active": -365.41,
"Reactive": -20.3,
"Apparent": 365.98
}
},
"L3": {
"Voltage": 239.9,
"Current": 1.31,
"Phi": 3.14,
"Power": {
"Active": -314.26,
"Reactive": 1.97,
"Apparent": 314.27
}
},
"Frequency": 50.0,
"Power": {
"Active": -1025.4,
"Reactive": 0.88,
"Apparent": 1025.4
}
},
"PowerLimitedBy": "DcLink",
"InverterState": {
"Current": "AcConnected",
"OnLastAlarm": "Alarm"
},
"ActiveGridType": "GridTied400V50Hz",
"DcVoltages": {
"Intern": {
"DcUpperHalf": 389.0,
"DcLowerHalf": 390.0,
"NToPe": 6553.5
},
"Extern": {
"UpperHalf": 389.0,
"LowerHalf": 390.0,
"NToPe": 0.7
},
"Active": {
"ActiveUpperVoltage": 780.0,
"ActiveLowerVoltage": 720.0,
"ActiveRefVoltage": 750.0
}
},
"Temperature": {
"InletAir": 23.9,
"IgbtL1": 49.7,
"IgbtL2": 47.6,
"IgbtL3": 51.8,
"IgbtBalancer": 50.6
},
"OverloadCapacity": {
"L1": 100.0,
"L2": 100.0,
"L3": 100.0
},
"Nominal": {
"AcFrequency": 50.0,
"AcVoltage": 400.0,
"Power": 25000.0
},
"Alarms": "",
"Warnings": ""
},
"Control": {
"Ac": {
"Power": {
"L1": {
"Active": 3.81,
"Reactive": 0.0,
"Apparent": 3.81
},
"L2": {
"Active": 3.81,
"Reactive": 0.0,
"Apparent": 3.81
},
"L3": {
"Active": 3.81,
"Reactive": 0.0,
"Apparent": 3.81
}
},
"PhaseControl": "Asymmetric",
"GridType": "GridTied400V50Hz",
"IslandMode": {
"FrequencyOffset": 0.0,
"VoltageAdjustmentFactor": 100.0
}
},
"Dc": {
"ReferenceVoltage": 750.0,
"MinVoltage": 720.0,
"MaxVoltage": 780.0,
"PrechargeConfig": "PrechargeDcWithInternal"
},
"PowerStageEnable": "True",
"ResetAlarmsAndWarnings": "False"
}
},
"2": {
"Status": {
"Ac": {
"L1": {
"Voltage": 239.0,
"Current": 0.74,
"Phi": -0.1,
"Power": {
"Active": 175.89,
"Reactive": -18.51,
"Apparent": 176.86
}
},
"L2": {
"Voltage": 239.5,
"Current": 0.62,
"Phi": -0.77,
"Power": {
"Active": 106.89,
"Reactive": -103.07,
"Apparent": 148.49
}
},
"L3": {
"Voltage": 239.8,
"Current": 0.47,
"Phi": -0.07,
"Power": {
"Active": 112.42,
"Reactive": -8.03,
"Apparent": 112.71
}
},
"Frequency": 50.01,
"Power": {
"Active": 395.2,
"Reactive": -129.62,
"Apparent": 415.91
}
},
"PowerLimitedBy": "Nothing",
"InverterState": {
"Current": "AcConnected",
"OnLastAlarm": "Idle"
},
"ActiveGridType": "GridTied400V50Hz",
"DcVoltages": {
"Intern": {
"DcUpperHalf": 388.0,
"DcLowerHalf": 389.0,
"NToPe": 0.1
},
"Extern": {
"UpperHalf": 388.0,
"LowerHalf": 388.0,
"NToPe": 6553.4
},
"Active": {
"ActiveUpperVoltage": 780.0,
"ActiveLowerVoltage": 720.0,
"ActiveRefVoltage": 750.0
}
},
"Temperature": {
"InletAir": 26.0,
"IgbtL1": 49.0,
"IgbtL2": 47.0,
"IgbtL3": 50.0,
"IgbtBalancer": 48.0
},
"OverloadCapacity": {
"L1": 100.0,
"L2": 100.0,
"L3": 100.0
},
"Nominal": {
"AcFrequency": 50.0,
"AcVoltage": 400.0,
"Power": 25000.0
},
"Alarms": "",
"Warnings": ""
},
"Control": {
"Ac": {
"Power": {
"L1": {
"Active": 3.81,
"Reactive": 0.0,
"Apparent": 3.81
},
"L2": {
"Active": 3.81,
"Reactive": 0.0,
"Apparent": 3.81
},
"L3": {
"Active": 3.81,
"Reactive": 0.0,
"Apparent": 3.81
}
},
"PhaseControl": "Asymmetric",
"GridType": "GridTied400V50Hz",
"IslandMode": {
"FrequencyOffset": 0.0,
"VoltageAdjustmentFactor": 100.0
}
},
"Dc": {
"ReferenceVoltage": 750.0,
"MinVoltage": 720.0,
"MaxVoltage": 780.0,
"PrechargeConfig": "PrechargeDcWithInternal"
},
"PowerStageEnable": "True",
"ResetAlarmsAndWarnings": "False"
}
}
},
"Alarms": "",
"Warnings": "",
"Ac": {
"L1": {
"Voltage": 238.9,
"Current": 2.19,
"Phi": 3.14,
"Power": {
"Active": -523.19,
"Reactive": 2.13,
"Apparent": 523.19
}
},
"L2": {
"Voltage": 239.35,
"Current": 2.15,
"Phi": -2.7,
"Power": {
"Active": -464.43,
"Reactive": -221.64,
"Apparent": 514.6
}
},
"L3": {
"Voltage": 239.85,
"Current": 1.78,
"Phi": -3.11,
"Power": {
"Active": -426.74,
"Reactive": -12.81,
"Apparent": 426.93
}
},
"Frequency": 50.0,
"Power": {
"Active": -1414.36,
"Reactive": -232.31,
"Apparent": 1433.31
}
},
"Dc": {
"Voltage": 777.5,
"Current": -1.82,
"Power": -1414.36
}
},
"DcDc": {
"Dc": {
"Link": {
"Voltage": 775.5,
"Current": 0.0,
"Power": 0.0
},
"Battery": {
"Voltage": 61.2,
"Current": 0.0,
"Power": 0.0
}
},
"SystemControl": {
"Alarms": "",
"CommunicationTimeout": "00:00:20",
"SystemConfig": "DcDcOnly",
"ResetAlarmsAndWarnings": "True",
"TargetSlave": 2.0,
"UseSlaveIdForAddressing": "True",
"ReferenceFrame": "Producer",
"SlaveErrorHandling": "Relaxed",
"SubSlaveErrorHandling": "Off",
"PowerSetPointActivation": "Immediate",
"PowerSetPointTrigger": "Wait",
"DeviceState": "Operation",
"NumberOfConnectedSlaves": 2.0,
"NumberOfConnectedSubSlaves": 0.0,
"Warnings": ""
},
"Devices": {
"1": {
"Status": {
"Dc": {
"Link": {
"Voltage": 775.0,
"Current": -0.0,
"Power": -0.0
},
"Battery": {
"Voltage": 61.2,
"Current": -0.0,
"Power": -0.0
}
},
"OverloadCapacity": 0.0,
"Temperature": {
"InletAir": 25.0,
"HighVoltageModule": 30.0,
"LowVoltageModule": 28.0
},
"PowerLimitedBy": "MaxChargeCurrent",
"Alarms": "",
"Warnings": ""
},
"Control": {
"Vcc": {
"EndPointCurrent": 50.0,
"EndPointVoltage": 50.0,
"StartPointCurrent": 5.0
},
"VoltageLimits": {
"MinBatteryVoltageAlarm": 0.0,
"MaxBatteryVoltageAlarm": 64.0,
"MinBatteryVoltage": 32.0,
"MaxBatteryVoltage": 63.0
},
"DroopControl": {
"ReferenceVoltage": 750.0,
"LowerVoltage": 20.0,
"UpperVoltage": 20.0,
"VoltageDeadband": 0.0
},
"CurrentControl": {
"CurrentSetpoint": 0.0,
"MaxCurrentChangePerMs": 100.0,
"MaxBatteryChargingCurrent": 0.0,
"MaxBatteryDischargingCurrent": 210.0
},
"MaxDcPower": 10000.0,
"ControlMode": "VoltageDroop",
"ResetAlarmsAndWarnings": "False",
"PowerStageEnable": "True"
}
},
"2": {
"Status": {
"Dc": {
"Link": {
"Voltage": 776.0,
"Current": -0.0,
"Power": -0.0
},
"Battery": {
"Voltage": 61.2,
"Current": -0.0,
"Power": -0.0
}
},
"OverloadCapacity": 0.0,
"Temperature": {
"InletAir": 23.0,
"HighVoltageModule": 30.0,
"LowVoltageModule": 29.0
},
"PowerLimitedBy": "MaxChargeCurrent",
"Alarms": "",
"Warnings": ""
},
"Control": {
"Vcc": {
"EndPointCurrent": 50.0,
"EndPointVoltage": 50.0,
"StartPointCurrent": 5.0
},
"VoltageLimits": {
"MinBatteryVoltageAlarm": 0.0,
"MaxBatteryVoltageAlarm": 64.0,
"MinBatteryVoltage": 32.0,
"MaxBatteryVoltage": 63.0
},
"DroopControl": {
"ReferenceVoltage": 750.0,
"LowerVoltage": 20.0,
"UpperVoltage": 20.0,
"VoltageDeadband": 0.0
},
"CurrentControl": {
"CurrentSetpoint": 0.0,
"MaxCurrentChangePerMs": 100.0,
"MaxBatteryChargingCurrent": 0.0,
"MaxBatteryDischargingCurrent": 210.0
},
"MaxDcPower": 10000.0,
"ControlMode": "VoltageDroop",
"ResetAlarmsAndWarnings": "False",
"PowerStageEnable": "True"
}
}
},
"Alarms": "",
"Warnings": ""
},
"Battery": {
"Current": -1.25,
"Voltage": 61.31,
"Soc": 99.85,
"Soh": 100.0,
"CurrentMinSoc": 99.3,
"TemperatureCell1": 23.35,
"Power": -76.62,
"LowestCellVoltage": 3.51,
"HighestCellVoltage": 3.89,
"MonomerHighVoltageAlarm": "True",
"MonomerLowVoltageAlarm": "False",
"ChargeSwitchState": "False",
"DischargeSwitchState": "True",
"AvailableDischBatteries": 6.0,
"AvailableChBatteries": 0.0,
"ChargeModeBatteries": 0.0,
"DischargeModeBatteries": 1.0,
"StandbyModeBatteries": 5.0,
"ShutDownModeBatteries": 0.0,
"Eoc": "False",
"Eod": "False",
"Devices": {
"1": {
"BatteryDeligreenDataRecord": {
"FwVersion": 2.0,
"BusVoltage": 61.33,
"BusCurrent": 0.0,
"Power": 0.0,
"TotalBatteryVoltage": 58.66,
"ResidualCapacity": 209.99,
"BatteryCapacity": 210.0,
"Soc": 99.9,
"RatedCapacity": 210.0,
"NumberOfCycles": 58.0,
"Soh": 100.0,
"CellVoltage": "3.742,3.564,3.887,3.644,3.599,3.574,3.601,3.555,3.528,3.58,3.823,3.753,3.693,3.771,3.569,3.778",
"TemperaturesList": {
"CellTemperature1": 22.2,
"CellTemperature2": 21.5,
"CellTemperature3": 21.5,
"CellTemperature4": 21.2,
"EnvironmentTemperature": 28.1,
"PowerTemperature": 23.0
}
},
"BatteryDeligreenAlarmRecord": {
"CellAlarmList": "Normal-no alarm,Normal-no alarm,Alarm that analog quantity reaches the upper limit,Normal-no alarm,Normal-no alarm,Normal-no alarm,Normal-no alarm,Normal-no alarm,Normal-no alarm,Normal-no alarm,Alarm that analog quantity reaches the upper limit,Normal-no alarm,Normal-no alarm,Alarm that analog quantity reaches the upper limit,Normal-no alarm,Alarm that analog quantity reaches the upper limit",
"CellTemperatureAlarm": "Normal-no alarm,Normal-no alarm,Normal-no alarm,Normal-no alarm",
"EnviTempAlarm": "Normal-no alarm",
"PowerTempAlarm": "Normal-no alarm",
"CurrentAlarm": "Normal-no alarm",
"TotalVoltageAlarm": "Normal-no alarm",
"AlarmEvent1": {
"VoltageSensorFault": "False",
"TemperatureSensorFault": "False",
"CurrentSensorFault": "False",
"KeySwitchFault": "False",
"CellVoltageDropoutFault": "False",
"ChargeSwitchFault": "False",
"DischargeSwitchFault": "False",
"CurrentLimitSwitchFault": "False"
},
"AlarmEvent2": {
"MonomerHighVoltageAlarm": "False",
"MonomerOvervoltageProtection": "True",
"MonomerLowVoltageAlarm": "False",
"MonomerUnderVoltageProtection": "False",
"HighVoltageAlarmForTotalVoltage": "False",
"OvervoltageProtectionForTotalVoltage": "False",
"LowVoltageAlarmForTotalVoltage": "False",
"UnderVoltageProtectionForTotalVoltage": "False"
},
"AlarmEvent3": {
"ChargeHighTemperatureAlarm": "False",
"ChargeOverTemperatureProtection": "False",
"ChargeLowTemperatureAlarm": "False",
"ChargeUnderTemperatureProtection": "False",
"DischargeHighTemperatureAlarm": "False",
"DischargeOverTemperatureProtection": "False",
"DischargeLowTemperatureAlarm": "False",
"DischargeUnderTemperatureProtection": "False"
},
"AlarmEvent4": {
"EnvironmentHighTemperatureAlarm": "False",
"EnvironmentOverTemperatureProtection": "False",
"EnvironmentLowTemperatureAlarm": "False",
"EnvironmentUnderTemperatureProtection": "False",
"PowerOverTemperatureProtection": "False",
"PowerHighTemperatureAlarm": "False",
"CellLowTemperatureHeating": "False",
"ReservationBit": "False"
},
"AlarmEvent5": {
"ChargeOverCurrentAlarm": "False",
"ChargeOverCurrentProtection": "False",
"DischargeOverCurrentAlarm": "False",
"DischargeOverCurrentProtection": "False",
"TransientOverCurrentProtection": "False",
"OutputShortCircuitProtection": "False",
"TransientOverCurrentLockout": "False",
"OutputShortCircuitLockout": "False"
},
"AlarmEvent6": {
"ChargeHighVoltageProtection": "False",
"IntermittentRechargeWaiting": "True",
"ResidualCapacityAlarm": "False",
"ResidualCapacityProtection": "False",
"CellLowVoltageChargingProhibition": "False",
"OutputReversePolarityProtection": "False",
"OutputConnectionFault": "False",
"InsideBit": "False"
},
"AlarmEvent7": {
"InsideBit1": "False",
"InsideBit2": "False",
"InsideBit3": "False",
"InsideBit4": "False",
"AutomaticChargingWaiting": "False",
"ManualChargingWaiting": "False",
"InsideBit5": "False",
"InsideBit6": "False"
},
"AlarmEvent8": {
"EepStorageFault": "False",
"RtcError": "False",
"VoltageCalibrationNotPerformed": "False",
"CurrentCalibrationNotPerformed": "False",
"ZeroCalibrationNotPerformed": "False",
"InsideBit1": "False",
"InsideBit2": "False",
"InsideBit3": "False"
},
"DisconnectionState1": {
"Cell01Disconnection": "False",
"Cell02Disconnection": "False",
"Cell03Disconnection": "False",
"Cell04Disconnection": "False",
"Cell05Disconnection": "False",
"Cell06Disconnection": "False",
"Cell07Disconnection": "False",
"Cell08Disconnection": "False"
},
"DisconnectionState2": {
"Cell09Disconnection": "False",
"Cell10Disconnection": "False",
"Cell11Disconnection": "False",
"Cell12Disconnection": "False",
"Cell13Disconnection": "False",
"Cell14Disconnection": "False",
"Cell15Disconnection": "False",
"Cell16Disconnection": "False"
},
"EquilibriumState1": {
"Cell01Equilibrium": "False",
"Cell02Equilibrium": "False",
"Cell03Equilibrium": "False",
"Cell04Equilibrium": "False",
"Cell05Equilibrium": "False",
"Cell06Equilibrium": "False",
"Cell07Equilibrium": "False",
"Cell08Equilibrium": "False"
},
"EquilibriumState2": {
"Cell09Equilibrium": "False",
"Cell10Equilibrium": "False",
"Cell11Equilibrium": "False",
"Cell12Equilibrium": "False",
"Cell13Equilibrium": "False",
"Cell14Equilibrium": "False",
"Cell15Equilibrium": "False",
"Cell16Equilibrium": "False"
},
"OnOffState": {
"DischargeSwitchState": "True",
"ChargeSwitchState": "False",
"CurrentLimitSwitchStat": "False",
"HeatingSwitchState": "False",
"ReservationBit1": "False",
"ReservationBit2": "False",
"ReservationBit3": "False",
"ReservationBit4": "False"
},
"SystemState": {
"Discharge": "False",
"Charge": "False",
"FloatingCharge": "False",
"ReservationBit1": "False",
"Standby": "True",
"Shutdown": "False",
"ReservationBit2": "False",
"ReservationBit3": "False"
}
}
},
"2" :{...}
}
},
"GridMeter": {
"Ac": {
"L1": {
"Voltage": 239.0,
"Current": 0.36,
"Phi": -1.55,
"Power": {
"Active": 1.99,
"Reactive": -86.59,
"Apparent": 86.61
}
},
"L2": {
"Voltage": 239.3,
"Current": 0.32,
"Phi": -1.66,
"Power": {
"Active": -7.02,
"Reactive": -77.2,
"Apparent": 77.52
}
},
"L3": {
"Voltage": 239.5,
"Current": 1.14,
"Phi": -1.96,
"Power": {
"Active": -103.28,
"Reactive": -252.68,
"Apparent": 272.97
}
},
"Frequency": 50.0,
"Power": {
"Active": -108.3,
"Reactive": -416.47,
"Apparent": 430.32
}
},
"ActivePowerImportT1": 28349.0,
"ActivePowerExportT1": 61296.0,
"ActivePowerImportT2": 28349.0,
"ActivePowerExportT2": 61296.0,
"ActivePowerImportT3": 46886.0,
"ActivePowerExportT3": 21032.0,
"ActivePowerImportT4": 0.0,
"ActivePowerExportT4": 0.0
},
"LoadOnAcIsland": {
"Ac": {
"L1": {
"Voltage": 239.0,
"Current": 0.0,
"Phi": 0.0,
"Power": {
"Active": 0.0,
"Reactive": 0.0,
"Apparent": 0.0
}
},
"L2": {
"Voltage": 239.3,
"Current": 0.0,
"Phi": 0.0,
"Power": {
"Active": 0.0,
"Reactive": 0.0,
"Apparent": 0.0
}
},
"L3": {
"Voltage": 239.5,
"Current": 0.0,
"Phi": 0.0,
"Power": {
"Active": 0.0,
"Reactive": 0.0,
"Apparent": 0.0
}
},
"Frequency": 50.0,
"Power": {
"Active": 0.0,
"Reactive": 0.0,
"Apparent": 0.0
}
},
"ActivePowerImportT1": 58082.0,
"ActivePowerExportT1": 1569.0,
"ActivePowerImportT2": 58082.0,
"ActivePowerExportT2": 1569.0,
"ActivePowerImportT3": 123.0,
"ActivePowerExportT3": 1.0,
"ActivePowerImportT4": 0.0,
"ActivePowerExportT4": 0.0
},
"LoadOnAcGrid": {
"Power": {
"Active": 1306.05,
"Reactive": -184.15,
"Apparent": 1318.97
}
},
"AcGridToAcIsland": {
"Power": {
"Active": -1414.36,
"Reactive": -232.31,
"Apparent": 1433.31
}
},
"AcDcToDcLink": {
"Power": -1414.36
},
"LoadOnDc": {
"Power": -321.32
},
"Relays": {
"K1GridBusIsConnectedToGrid": "True",
"K2IslandBusIsConnectedToGridBus": "True",
"K3Inverter1IsConnectedToIslandBus": "True",
"K3Inverter2IsConnectedToIslandBus": "True",
"K3Inverter3IsConnectedToIslandBus": "True",
"K3Inverter4IsConnectedToIslandBus": "True",
"FiWarning": "False",
"FiError": "False",
"K2ConnectIslandBusToGridBus": "True"
},
"PvOnDc": {
"Dc": {
"Voltage": 779.07,
"Current": 1.4,
"Power": 1093.03
},
"NbrOfStrings": 3.0,
"Strings": {
"1": {
"Voltage": 779.25,
"Current": 0.5,
"Power": 391.96
},
"2": {
"Voltage": 779.1,
"Current": 0.52,
"Power": 407.47
},
"3": {
"Voltage": 778.86,
"Current": 0.38,
"Power": 293.63
}
},
"DcWh": 137.11
},
"Config": {
"MinSoc": 10.0,
"CurtailP": 5.0,
"PvInstalledPower": 0.0,
"ForceCalibrationChargeState": "RepetitivelyEvery",
"DayAndTimeForRepetitiveCalibration": "05/29/2025 09:00:00",
"DayAndTimeForAdditionalCalibration": "05/23/2025 15:46:00",
"ForceCalibrationDischargeState": "RepetitivelyEvery",
"DownDayAndTimeForRepetitiveCalibration": "06/23/2025 19:00:00",
"DownDayAndTimeForAdditionalCalibration": "01/01/0001 00:00:00",
"DisplayIndividualBatteries": "False",
"PConstant": 0.5,
"GridSetPoint": 0.0,
"BatterySelfDischargePower": 200.0,
"HoldSocZone": 1.0,
"IslandMode": {
"AcDc": {
"MaxDcLinkVoltage": 930.0,
"MinDcLinkVoltage": 690.0,
"ReferenceDcLinkVoltage": 750.0
},
"DcDc": {
"LowerDcLinkVoltage": 50.0,
"ReferenceDcLinkVoltage": 750.0,
"UpperDcLinkVoltage": 50.0,
"MaxBatteryChargingCurrent": 210.0,
"MaxBatteryDischargingCurrent": 210.0,
"MaxDcPower": 10000.0,
"MaxChargeBatteryVoltage": 63.0,
"MinDischargeBatteryVoltage": 32.0
}
},
"GridTie": {
"AcDc": {
"MaxDcLinkVoltage": 780.0,
"MinDcLinkVoltage": 720.0,
"ReferenceDcLinkVoltage": 750.0
},
"DcDc": {
"LowerDcLinkVoltage": 20.0,
"ReferenceDcLinkVoltage": 750.0,
"UpperDcLinkVoltage": 20.0,
"MaxBatteryChargingCurrent": 210.0,
"MaxBatteryDischargingCurrent": 210.0,
"MaxDcPower": 10000.0,
"MaxChargeBatteryVoltage": 63.0,
"MinDischargeBatteryVoltage": 32.0
}
},
"Devices": {
"RelaysIp": {
"DeviceState": "Measured",
"Host": "10.0.1.1",
"Port": 502.0
},
"TsRelaysIp": {
"DeviceState": "Measured",
"Host": "10.0.1.2",
"Port": 502.0
},
"GridMeterIp": {
"DeviceState": "Measured",
"Host": "10.0.4.1",
"Port": 502.0
},
"PvOnAcGrid": {
"DeviceState": "Disabled",
"Host": "false",
"Port": 0.0
},
"LoadOnAcGrid": {
"DeviceState": "Measured",
"Host": "true",
"Port": 0.0
},
"PvOnAcIsland": {
"DeviceState": "Disabled",
"Host": "false",
"Port": 0.0
},
"IslandBusLoadMeterIp": {
"DeviceState": "Measured",
"Host": "10.0.4.2",
"Port": 502.0
},
"TruConvertAcIp": {
"DeviceState": "Measured",
"Host": "10.0.2.1",
"Port": 502.0
},
"PvOnDc": {
"DeviceState": "Measured",
"Host": "10.0.5.1",
"Port": 502.0
},
"LoadOnDc": {
"DeviceState": "Measured",
"Host": "false",
"Port": 0.0
},
"TruConvertDcIp": {
"DeviceState": "Measured",
"Host": "10.0.3.1",
"Port": 502.0
},
"BatteryIp": {
"DeviceState": "Measured",
"Host": "localhost",
"Port": 6855.0
},
"BatteryNodes": "0,1,2,3,4,5"
},
"S3": {
"Bucket": "1-3e5b3069-214a-43ee-8d85-57d72000c19d",
"Region": "sos-ch-dk-2",
"Provider": "exo.io",
"Key": "EXO1752677fa3b7a5dc1b4efcb9",
"Secret": "fUTGbI-I29dPdS9KrJxpnrpBcWbcpAZmoYcECfVEiYU",
"ContentType": "text/plain",
"Host": "1-3e5b3069-214a-43ee-8d85-57d72000c19d.sos-ch-dk-2.exo.io",
"Url": "https://1-3e5b3069-214a-43ee-8d85-57d72000c19d.sos-ch-dk-2.exo.io"
}
},
"Log": {
"SalimaxAlarmState": "Green"
},
"EssControl": {
"Mode": "UpwardsCalibrationCharge",
"LimitedBy": "ChargeLimitedByMaxDcBusVoltage",
"PowerCorrection": -355.15,
"PowerSetpoint": 22.85
},
"StateMachine": {
"Message": "ESS",
"State": 23.0
}
}

View File

@ -0,0 +1,256 @@
import os
import csv
import subprocess
import argparse
import matplotlib.pyplot as plt
from collections import defaultdict
import zipfile
import base64
import shutil
import json
import sys
def extract_timestamp(filename):
timestamp_str = filename[:10]
try:
timestamp = int(timestamp_str)
return timestamp
except ValueError:
return 0
def list_files_in_range(start_timestamp, end_timestamp, sampling_stepsize,product_type,bucket_number):
if product_type == "Salimax" or product_type=="SodistoreMax":
hash = "3e5b3069-214a-43ee-8d85-57d72000c19d"
elif product_type == "Salidomo":
hash = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
else:
raise ValueError("Invalid product type option. Use Salimax or Salidomo or SodistoreMax")
# Find common prefix
common_prefix = ""
for s_char, e_char in zip(str(start_timestamp), str(end_timestamp)):
if s_char == e_char:
common_prefix += s_char
else:
break
s3_path = f"s3://{bucket_number}-{hash}/{common_prefix}*"
s3cmd_command = f"s3cmd ls {s3_path}"
print(f"Running: {s3cmd_command}")
try:
output = subprocess.check_output(s3cmd_command, shell=True, text=True)
files = [line.split()[-1] for line in output.strip().split("\n") if line.strip()]
filenames = []
for f in files:
name = f.split("/")[-1] # e.g., 1748802020.json
timestamp_str = name.split(".")[0] # extract '1748802020'
if timestamp_str.isdigit() and int(timestamp_str) <= int(end_timestamp):
filenames.append(name)
else:
break
print(filenames)
return filenames
except subprocess.CalledProcessError:
print(f"No files found for prefix {common_prefix}")
return []
def get_nested_value(data, key_path):
try:
for key in key_path:
data = data[key]
return data
except (KeyError, TypeError):
return None
def process_json_files_to_csv(output_directory, json_files, keys, start_timestamp, end_timestamp, bucket_number, booleans_as_numbers):
# Generate output file name from all keys
keypath = '_'.join(get_last_component(k) for k in keys)
output_csv_filename = f"{keypath}_from_{start_timestamp}_to_{end_timestamp}_bucket_{bucket_number}.csv"
with open(output_csv_filename, 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
# Write header: 'time' + key names
header = ['time'] + [k.split('/')[-1] for k in keys]
csv_writer.writerow(header)
for json_file in json_files:
file_path = os.path.join(output_directory, json_file)
with open(file_path, 'r') as f:
lines = f.readlines()
i = 0
while i < len(lines) - 1:
timestamp_line = lines[i].strip()
json_line = lines[i + 1].strip()
i += 2
if not timestamp_line.startswith("Timestamp;"):
continue
try:
timestamp = int(timestamp_line.split(';')[1])
except ValueError:
continue
if not (start_timestamp <= timestamp <= end_timestamp):
continue
try:
data = json.loads(json_line)
except json.JSONDecodeError:
print(f"❌ Failed to parse JSON in {json_file}, line {i}")
continue
row = [timestamp]
for key in keys:
value = get_nested_value(data, key.split('/'))
if booleans_as_numbers and isinstance(value, str) and value.lower() in ["true", "false"]:
value = 1 if value.lower() == "true" else 0
if value is None:
value = "No value provided"
row.append(value)
csv_writer.writerow(row)
print(f"✅ Extracted data saved in '{output_csv_filename}'")
def download_files(bucket_number, filenames_to_download, product_type):
if product_type == "Salimax" or product_type=="SodistoreMax":
hash = "3e5b3069-214a-43ee-8d85-57d72000c19d"
elif product_type == "Salidomo":
hash = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
else:
raise ValueError("Invalid product type option. Use Salimax or Salidomo or SodistoreMax")
output_directory = f"S3cmdData_{bucket_number}"
if not os.path.exists(output_directory):
os.makedirs(output_directory)
print(f"Directory '{output_directory}' created.")
for filename in filenames_to_download:
print(filename)
local_path = os.path.join(output_directory, filename)
if not os.path.exists(local_path):
s3cmd_command = f"s3cmd get s3://{bucket_number}-{hash}/{filename} {output_directory}/"
try:
subprocess.run(s3cmd_command, shell=True, check=True)
downloaded_files = [file for file in os.listdir(output_directory) if file.startswith(filename)]
if not downloaded_files:
print(f"No matching files found for prefix '{filename}'.")
else:
print(f"Files with prefix '{filename}' downloaded successfully.")
decompress_file(os.path.join(output_directory, filename), output_directory)
except subprocess.CalledProcessError as e:
# print(f"Error downloading files: {e}")
continue
else:
print(f"File '{filename}.json' already exists locally. Skipping download.")
def decompress_file(compressed_file, output_directory):
base_name = os.path.splitext(os.path.basename(compressed_file))[0]
with open(compressed_file, 'rb') as file:
compressed_data = file.read()
# Decode the base64 encoded content
decoded_data = base64.b64decode(compressed_data)
zip_path = os.path.join(output_directory, 'temp.zip')
with open(zip_path, 'wb') as zip_file:
zip_file.write(decoded_data)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(output_directory)
# Rename the extracted data.json file to the original timestamp-based name
extracted_csv_path = os.path.join(output_directory, 'data.json')
if os.path.exists(extracted_csv_path):
new_csv_path = os.path.join(output_directory, f"{base_name}.json")
os.rename(extracted_csv_path, new_csv_path)
os.remove(zip_path)
print(f"Decompressed and renamed '{compressed_file}' to '{new_csv_path}'.")
def get_last_component(path):
path_without_slashes = path.replace('/', '')
return path_without_slashes
def download_and_process_files(bucket_number, start_timestamp, end_timestamp, sampling_stepsize, keys, booleans_as_numbers, exact_match, product_type):
output_directory = f"S3cmdData_{bucket_number}"
#if os.path.exists(output_directory):
# shutil.rmtree(output_directory)
if not os.path.exists(output_directory):
os.makedirs(output_directory)
print(f"Directory '{output_directory}' created.")
filenames_to_check = list_files_in_range(start_timestamp, end_timestamp, sampling_stepsize,product_type,bucket_number)
existing_files = [filename for filename in filenames_to_check if os.path.exists(os.path.join(output_directory, f"{filename}.json"))]
files_to_download = set(filenames_to_check) - set(existing_files)
print(files_to_download)
#if os.listdir(output_directory):
# print("Files already exist in the local folder. Skipping download.")
#else:
if files_to_download:
download_files(bucket_number, files_to_download, product_type)
json_files = [file for file in os.listdir(output_directory) if file.endswith('.json')]
json_files.sort(key=extract_timestamp)
process_json_files_to_csv(
output_directory=output_directory,
json_files=json_files,
keys=keys,
start_timestamp=start_timestamp,
end_timestamp=end_timestamp,
bucket_number=bucket_number,
booleans_as_numbers=booleans_as_numbers
)
def parse_keys(input_string):
keys = [key.strip() for key in input_string.split(',')]
return keys
def main():
parser = argparse.ArgumentParser(description='Download files from S3 using s3cmd and extract specific values from CSV files.')
parser.add_argument('start_timestamp', type=int, help='The start timestamp for the range (even number)')
parser.add_argument('end_timestamp', type=int, help='The end timestamp for the range (even number)')
parser.add_argument('--keys', type=parse_keys, required=True, help='The part to match from each CSV file, can be a single key or a comma-separated list of keys')
parser.add_argument('--bucket-number', type=int, required=True, help='The number of the bucket to download from')
parser.add_argument('--sampling_stepsize', type=int, required=False, default=1, help='The number of 2sec intervals, which define the length of the sampling interval in S3 file retrieval')
parser.add_argument('--booleans_as_numbers', action="store_true", required=False, help='If key used, then booleans are converted to numbers [0/1], if key not used, then booleans maintained as text [False/True]')
parser.add_argument('--exact_match', action="store_true", required=False, help='If key used, then key has to match exactly "=", else it is enough that key is found "in" text')
parser.add_argument('--product_name', required=True, help='Use Salimax, Salidomo or SodistoreMax')
args = parser.parse_args()
start_timestamp = args.start_timestamp
end_timestamp = args.end_timestamp
keys = args.keys
bucket_number = args.bucket_number
sampling_stepsize = args.sampling_stepsize
booleans_as_numbers = args.booleans_as_numbers
exact_match = args.exact_match
# new arg for product type
product_type = args.product_name
if start_timestamp >= end_timestamp:
print("Error: start_timestamp must be smaller than end_timestamp.")
return
download_and_process_files(bucket_number, start_timestamp, end_timestamp, sampling_stepsize, keys, booleans_as_numbers, exact_match, product_type)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,94 @@
using System.Diagnostics;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.Backend.DeleteOldData;
public class DeleteOldDataFromS3
{
public static void DeleteFrom(Installation installation, int timestamps_to_delete)
{
string configPath = "/home/ubuntu/.s3cfg";
string bucketPath = installation.Product ==(int)ProductType.Salidomo ? $"s3://{installation.S3BucketId}-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/{timestamps_to_delete}*" : $"s3://{installation.S3BucketId}-3e5b3069-214a-43ee-8d85-57d72000c19d/{timestamps_to_delete}*" ;
//Console.WriteLine($"Deleting old data from {bucketPath}");
Console.WriteLine("Deleting data for timestamp prefix: " + timestamps_to_delete);
try
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "s3cmd",
Arguments = $"--config {configPath} rm {bucketPath}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using Process process = new Process { StartInfo = startInfo };
process.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
Console.WriteLine("[s3cmd] " + e.Data);
};
process.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
Console.WriteLine("[s3cmd-ERR] " + e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred during deletion: " + ex.Message);
}
}
public static async Task DeleteOldData()
{
while (true){
var installations = Db.Installations.ToList();
foreach (var installation in installations){
Console.WriteLine("DELETE S3 DATA FOR INSTALLATION "+installation.Name);
long oneYearAgoTimestamp = DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds();
Console.WriteLine("delete data before "+oneYearAgoTimestamp);
for (int lastDigit=4;lastDigit>=0; lastDigit--)
{
int timestamps_to_delete = int.Parse(oneYearAgoTimestamp.ToString().Substring(0, lastDigit+1));
timestamps_to_delete--;
Console.WriteLine(timestamps_to_delete);
while (true)
{
if (timestamps_to_delete % 10 == 0)
{
Console.WriteLine("delete " + timestamps_to_delete + "*");
DeleteFrom(installation,timestamps_to_delete);
break;
}
Console.WriteLine("delete " + timestamps_to_delete + "*");
DeleteFrom(installation,timestamps_to_delete);
timestamps_to_delete--;
}
}
}
Console.WriteLine("FINISHED DELETING S3 DATA FOR ALL INSTALLATIONS\n");
await Task.Delay(TimeSpan.FromDays(1));
}
}
}

View File

@ -3,6 +3,7 @@ using Flurl.Http;
using Hellang.Middleware.ProblemDetails;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.Websockets;
using InnovEnergy.App.Backend.DeleteOldData;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
@ -30,6 +31,9 @@ public static class Program
WebsocketManager.MonitorSalimaxInstallationTable().SupressAwaitWarning();
WebsocketManager.MonitorSalidomoInstallationTable().SupressAwaitWarning();
WebsocketManager.MonitorSodistoreInstallationTable().SupressAwaitWarning();
Task.Run(() => DeleteOldDataFromS3.DeleteOldData());
builder.Services.AddControllers();
builder.Services.AddProblemDetails(setup =>

View File

@ -19,7 +19,7 @@ public static class WebsocketManager
{
while (true){
lock (InstallationConnections){
Console.WriteLine("MONITOR SALIMAX INSTALLATIONS\n");
// Console.WriteLine("MONITOR SALIMAX INSTALLATIONS\n");
foreach (var installationConnection in InstallationConnections){
if (installationConnection.Value.Product==(int)ProductType.Salimax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)){
@ -35,7 +35,7 @@ public static class WebsocketManager
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
}
}
Console.WriteLine("FINISHED MONITORING SALIMAX INSTALLATIONS\n");
// Console.WriteLine("FINISHED MONITORING SALIMAX INSTALLATIONS\n");
}
await Task.Delay(TimeSpan.FromMinutes(1));
@ -47,15 +47,15 @@ public static class WebsocketManager
public static async Task MonitorSalidomoInstallationTable()
{
while (true){
Console.WriteLine("TRY TO LOCK FOR MONITOR SALIDOMO INSTALLATIONS\n");
//Console.WriteLine("TRY TO LOCK FOR MONITOR SALIDOMO INSTALLATIONS\n");
lock (InstallationConnections){
Console.WriteLine("MONITOR SALIDOMO INSTALLATIONS\n");
//Console.WriteLine("MONITOR SALIDOMO INSTALLATIONS\n");
foreach (var installationConnection in InstallationConnections)
{
//Console.WriteLine("Installation ID is "+installationConnection.Key);
if (installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) < TimeSpan.FromMinutes(60)){
Console.WriteLine("Installation ID is "+installationConnection.Key + " diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
}
// if (installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) < TimeSpan.FromMinutes(60)){
// Console.WriteLine("Installation ID is "+installationConnection.Key + " diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
// }
if (installationConnection.Value.Product==(int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60))
{
@ -64,16 +64,16 @@ public static class WebsocketManager
//Console.WriteLine("timestamp now is is "+(DateTime.Now));
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == (int)ProductType.Salidomo && f.Id == installationConnection.Key);
Console.WriteLine("Installation ID is "+installation.Name + " diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
//Console.WriteLine("Installation ID is "+installation.Name + " diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
installation.Status = (int)StatusType.Offline;
installation.Apply(Db.Update);
installationConnection.Value.Status = (int)StatusType.Offline;
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
else{Console.WriteLine("NONE IS CONNECTED TO THAT INSTALLATION-------------------------------------------------------------");}
//else{Console.WriteLine("NONE IS CONNECTED TO THAT INSTALLATION-------------------------------------------------------------");}
}
}
Console.WriteLine("FINISHED WITH UPDATING\n");
//Console.WriteLine("FINISHED WITH UPDATING\n");
}
await Task.Delay(TimeSpan.FromMinutes(1));
}
@ -84,9 +84,9 @@ public static class WebsocketManager
public static async Task MonitorSodistoreInstallationTable()
{
while (true){
Console.WriteLine("TRY TO LOCK FOR MONITOR SODISTORE INSTALLATIONS\n");
//Console.WriteLine("TRY TO LOCK FOR MONITOR SODISTORE INSTALLATIONS\n");
lock (InstallationConnections){
Console.WriteLine("MONITOR SODISTORE INSTALLATIONS\n");
//Console.WriteLine("MONITOR SODISTORE INSTALLATIONS\n");
foreach (var installationConnection in InstallationConnections)
{
@ -97,16 +97,16 @@ public static class WebsocketManager
//Console.WriteLine("timestamp now is is "+(DateTime.Now));
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == (int)ProductType.SodiStoreMax && f.Id == installationConnection.Key);
Console.WriteLine("Installation ID is "+installation.Name + " diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
//Console.WriteLine("Installation ID is "+installation.Name + " diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
installation.Status = (int)StatusType.Offline;
installation.Apply(Db.Update);
installationConnection.Value.Status = (int)StatusType.Offline;
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
else{Console.WriteLine("NONE IS CONNECTED TO THAT INSTALLATION-------------------------------------------------------------");}
//else{Console.WriteLine("NONE IS CONNECTED TO THAT INSTALLATION-------------------------------------------------------------");}
}
}
Console.WriteLine("FINISHED WITH UPDATING\n");
//Console.WriteLine("FINISHED WITH UPDATING\n");
}
await Task.Delay(TimeSpan.FromMinutes(1));
}
@ -225,20 +225,20 @@ public static class WebsocketManager
);
Console.WriteLine("Printing installation connection list");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in InstallationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
// Console.WriteLine("Printing installation connection list");
// Console.WriteLine("----------------------------------------------");
// foreach (var installationConnection in InstallationConnections)
// {
// Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
// }
// Console.WriteLine("----------------------------------------------");
}
}
lock (InstallationConnections)
{
//When the front-end terminates the connection, the following code will be executed
Console.WriteLine("The connection has been terminated");
//Console.WriteLine("The connection has been terminated");
foreach (var installationConnection in InstallationConnections)
{
if (installationConnection.Value.Connections.Contains(currentWebSocket))
@ -249,18 +249,18 @@ public static class WebsocketManager
}
await currentWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed by server", CancellationToken.None);
lock (InstallationConnections)
{
//Print the installationConnections dictionary after deleting a websocket
Console.WriteLine("Print the installation connections list after deleting a websocket");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in InstallationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
// lock (InstallationConnections)
// {
// //Print the installationConnections dictionary after deleting a websocket
// Console.WriteLine("Print the installation connections list after deleting a websocket");
// Console.WriteLine("----------------------------------------------");
// foreach (var installationConnection in InstallationConnections)
// {
// Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
// }
//
// Console.WriteLine("----------------------------------------------");
// }
}
catch (Exception ex)
{

View File

@ -37,6 +37,7 @@
"react-cytoscapejs": "^2.0.0",
"react-dom": "17.0.2",
"react-flow-renderer": "^10.3.17",
"react-gauge-chart": "^0.5.1",
"react-helmet-async": "1.3.0",
"react-icons": "^4.11.0",
"react-icons-converter": "^1.1.4",
@ -7737,6 +7738,51 @@
"resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
"integrity": "sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg=="
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-axis": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-brush": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-drag": "2 - 3",
"d3-interpolate": "1 - 3",
"d3-selection": "3",
"d3-transition": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-chord": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
"dependencies": {
"d3-path": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
@ -7745,6 +7791,28 @@
"node": ">=12"
}
},
"node_modules/d3-contour": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
"dependencies": {
"d3-array": "^3.2.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-delaunay": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
"dependencies": {
"delaunator": "5"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dispatch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
@ -7765,6 +7833,38 @@
"node": ">=12"
}
},
"node_modules/d3-dsv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
"dependencies": {
"commander": "7",
"iconv-lite": "0.6",
"rw": "1"
},
"bin": {
"csv2json": "bin/dsv2json.js",
"csv2tsv": "bin/dsv2dsv.js",
"dsv2dsv": "bin/dsv2dsv.js",
"dsv2json": "bin/dsv2json.js",
"json2csv": "bin/json2dsv.js",
"json2dsv": "bin/json2dsv.js",
"json2tsv": "bin/json2dsv.js",
"tsv2csv": "bin/dsv2dsv.js",
"tsv2json": "bin/dsv2json.js"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dsv/node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"engines": {
"node": ">= 10"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
@ -7773,6 +7873,57 @@
"node": ">=12"
}
},
"node_modules/d3-fetch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
"dependencies": {
"d3-dsv": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-force": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-quadtree": "1 - 3",
"d3-timer": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-geo": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
"dependencies": {
"d3-array": "2.5.0 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-hierarchy": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
@ -7784,6 +7935,65 @@
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-polygon": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-quadtree": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-random": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale-chromatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
"dependencies": {
"d3-color": "1 - 3",
"d3-interpolate": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-selection": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
@ -7792,6 +8002,39 @@
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
@ -8022,6 +8265,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
"dependencies": {
"robust-predicates": "^3.0.2"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -11005,6 +11256,14 @@
"node": ">= 0.4"
}
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"engines": {
"node": ">=12"
}
},
"node_modules/intl-messageformat": {
"version": "10.5.14",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz",
@ -17037,6 +17296,58 @@
"react-dom": "16 || 17 || 18"
}
},
"node_modules/react-gauge-chart": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-gauge-chart/-/react-gauge-chart-0.5.1.tgz",
"integrity": "sha512-Kd0pLIfHWOVEWJCz+xAxFfrKzCdOkLHNKR13V+O3h06ocyGT7fWjz2oevMXsPt3bVn1Du2od7gPrjUhqXKdASg==",
"dependencies": {
"d3": "^7.6.1"
},
"peerDependencies": {
"react": "^16.8.2 || ^17.0 || ^18.x",
"react-dom": "^16.8.2 || ^17.0 || ^18.x"
}
},
"node_modules/react-gauge-chart/node_modules/d3": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
"dependencies": {
"d3-array": "3",
"d3-axis": "3",
"d3-brush": "3",
"d3-chord": "3",
"d3-color": "3",
"d3-contour": "4",
"d3-delaunay": "6",
"d3-dispatch": "3",
"d3-drag": "3",
"d3-dsv": "3",
"d3-ease": "3",
"d3-fetch": "3",
"d3-force": "3",
"d3-format": "3",
"d3-geo": "3",
"d3-hierarchy": "3",
"d3-interpolate": "3",
"d3-path": "3",
"d3-polygon": "3",
"d3-quadtree": "3",
"d3-random": "3",
"d3-scale": "4",
"d3-scale-chromatic": "3",
"d3-selection": "3",
"d3-shape": "3",
"d3-time": "3",
"d3-time-format": "4",
"d3-timer": "3",
"d3-transition": "3",
"d3-zoom": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/react-helmet-async": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz",
@ -17652,6 +17963,11 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
},
"node_modules/rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
@ -17750,6 +18066,11 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
@ -26418,11 +26739,60 @@
"resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
"integrity": "sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg=="
},
"d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"requires": {
"internmap": "1 - 2"
}
},
"d3-axis": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="
},
"d3-brush": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
"requires": {
"d3-dispatch": "1 - 3",
"d3-drag": "2 - 3",
"d3-interpolate": "1 - 3",
"d3-selection": "3",
"d3-transition": "3"
}
},
"d3-chord": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
"requires": {
"d3-path": "1 - 3"
}
},
"d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
},
"d3-contour": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
"requires": {
"d3-array": "^3.2.0"
}
},
"d3-delaunay": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
"requires": {
"delaunator": "5"
}
},
"d3-dispatch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
@ -26437,11 +26807,64 @@
"d3-selection": "3"
}
},
"d3-dsv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
"requires": {
"commander": "7",
"iconv-lite": "0.6",
"rw": "1"
},
"dependencies": {
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
}
}
},
"d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
},
"d3-fetch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
"requires": {
"d3-dsv": "1 - 3"
}
},
"d3-force": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
"requires": {
"d3-dispatch": "1 - 3",
"d3-quadtree": "1 - 3",
"d3-timer": "1 - 3"
}
},
"d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
},
"d3-geo": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
"requires": {
"d3-array": "2.5.0 - 3"
}
},
"d3-hierarchy": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="
},
"d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
@ -26450,11 +26873,76 @@
"d3-color": "1 - 3"
}
},
"d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="
},
"d3-polygon": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="
},
"d3-quadtree": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="
},
"d3-random": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="
},
"d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"requires": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
}
},
"d3-scale-chromatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
"requires": {
"d3-color": "1 - 3",
"d3-interpolate": "1 - 3"
}
},
"d3-selection": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
},
"d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"requires": {
"d3-path": "^3.1.0"
}
},
"d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"requires": {
"d3-array": "2 - 3"
}
},
"d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"requires": {
"d3-time": "1 - 3"
}
},
"d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
@ -26616,6 +27104,14 @@
"object-keys": "^1.1.1"
}
},
"delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
"requires": {
"robust-predicates": "^3.0.2"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -28819,6 +29315,11 @@
"side-channel": "^1.0.4"
}
},
"internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="
},
"intl-messageformat": {
"version": "10.5.14",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz",
@ -33141,6 +33642,53 @@
"zustand": "^3.7.2"
}
},
"react-gauge-chart": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-gauge-chart/-/react-gauge-chart-0.5.1.tgz",
"integrity": "sha512-Kd0pLIfHWOVEWJCz+xAxFfrKzCdOkLHNKR13V+O3h06ocyGT7fWjz2oevMXsPt3bVn1Du2od7gPrjUhqXKdASg==",
"requires": {
"d3": "^7.6.1"
},
"dependencies": {
"d3": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
"requires": {
"d3-array": "3",
"d3-axis": "3",
"d3-brush": "3",
"d3-chord": "3",
"d3-color": "3",
"d3-contour": "4",
"d3-delaunay": "6",
"d3-dispatch": "3",
"d3-drag": "3",
"d3-dsv": "3",
"d3-ease": "3",
"d3-fetch": "3",
"d3-force": "3",
"d3-format": "3",
"d3-geo": "3",
"d3-hierarchy": "3",
"d3-interpolate": "3",
"d3-path": "3",
"d3-polygon": "3",
"d3-quadtree": "3",
"d3-random": "3",
"d3-scale": "4",
"d3-scale-chromatic": "3",
"d3-selection": "3",
"d3-shape": "3",
"d3-time": "3",
"d3-time-format": "4",
"d3-timer": "3",
"d3-transition": "3",
"d3-zoom": "3"
}
}
}
},
"react-helmet-async": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz",
@ -33586,6 +34134,11 @@
"glob": "^7.1.3"
}
},
"robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
},
"rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
@ -33653,6 +34206,11 @@
"queue-microtask": "^1.2.2"
}
},
"rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
},
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",

View File

@ -16,6 +16,7 @@
"axios": "^1.5.0",
"chart.js": "^4.4.0",
"clsx": "1.1.1",
"crypto-js": "^4.2.0",
"cytoscape": "^3.26.0",
"date-fns": "^2.28.0",
"dayjs": "^1.11.10",
@ -32,6 +33,7 @@
"react-cytoscapejs": "^2.0.0",
"react-dom": "17.0.2",
"react-flow-renderer": "^10.3.17",
"react-gauge-chart": "^0.5.1",
"react-helmet-async": "1.3.0",
"react-icons": "^4.11.0",
"react-icons-converter": "^1.1.4",

View File

@ -6,4 +6,5 @@
.apexcharts-toolbar {
display: none !important;
}
}

View File

@ -27,6 +27,7 @@ import CircularProgress from '@mui/material/CircularProgress';
import MainStats from './MainStats';
import DetailedBatteryView from './DetailedBatteryView';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
import DetailedBatteryViewSodistore from './DetailedBatteryViewSodistore';
interface BatteryViewProps {
values: JSONRecordData;
@ -175,23 +176,41 @@ function BatteryView(props: BatteryViewProps) {
></MainStats>
}
/>
{Object.entries(props.values.Battery.Devices).map(
([BatteryId, battery]) => (
<Route
key={routes.detailed_view + BatteryId}
path={routes.detailed_view + BatteryId}
element={
<DetailedBatteryView
batteryId={Number(BatteryId)}
s3Credentials={props.s3Credentials}
batteryData={battery}
installationId={props.installationId}
productNum={product}
></DetailedBatteryView>
}
/>
)
)}
{product === 0
? Object.entries(props.values.Battery.Devices).map(
([BatteryId, battery]) => (
<Route
key={routes.detailed_view + BatteryId}
path={routes.detailed_view + BatteryId}
element={
<DetailedBatteryView
batteryId={Number(BatteryId)}
s3Credentials={props.s3Credentials}
batteryData={battery}
installationId={props.installationId}
productNum={product}
></DetailedBatteryView>
}
/>
)
)
: Object.entries(props.values.Battery.Devices).map(
([BatteryId, battery]) => (
<Route
key={routes.detailed_view + BatteryId}
path={routes.detailed_view + BatteryId}
element={
<DetailedBatteryViewSodistore
batteryId={Number(BatteryId)}
s3Credentials={props.s3Credentials}
batteryData={battery}
installationId={props.installationId}
productNum={product}
></DetailedBatteryViewSodistore>
}
/>
)
)}
</Routes>
</Grid>
@ -226,6 +245,9 @@ function BatteryView(props: BatteryViewProps) {
) : (
<TableCell align="center">Max Cell Voltage</TableCell>
)}
{product === 3 && (
<TableCell align="center">Voltage Difference</TableCell>
)}
</TableRow>
</TableHead>
<TableBody>
@ -350,14 +372,21 @@ function BatteryView(props: BatteryViewProps) {
: '#32CD32'
};
} else {
const avgTemp =
(battery.BatteryDeligreenDataRecord.TemperaturesList
.CellTemperature1 +
battery.BatteryDeligreenDataRecord
.TemperaturesList.CellTemperature2 +
battery.BatteryDeligreenDataRecord
.TemperaturesList.CellTemperature3 +
battery.BatteryDeligreenDataRecord
.TemperaturesList.CellTemperature4) /
4;
return {
width: '10%',
textAlign: 'center',
backgroundColor:
battery.BatteryDeligreenDataRecord
.TemperaturesList.EnvironmentTemperature > 50 ||
battery.BatteryDeligreenDataRecord
.TemperaturesList.EnvironmentTemperature < 0
avgTemp > 50 || avgTemp < 0
? '#FF033E'
: '#32CD32'
};
@ -459,13 +488,13 @@ function BatteryView(props: BatteryViewProps) {
<>
<TableCell
sx={{
width: '20%',
width: '15%',
textAlign: 'center',
padding: '8px',
backgroundColor:
minVoltage < 1.82 || minVoltage > 3.94
minVoltage < 2
? '#FF033E'
: minVoltage < 2 || minVoltage > 3.8
: minVoltage > 2 && minVoltage < 2.2
? '#ffbf00'
: '#32CD32',
color: minVoltage != 0 ? 'black' : 'white'
@ -476,13 +505,13 @@ function BatteryView(props: BatteryViewProps) {
<TableCell
sx={{
width: '20%',
width: '15%',
textAlign: 'center',
padding: '8px',
backgroundColor:
maxVoltage < 1.82 || maxVoltage > 3.94
maxVoltage > 3.9
? '#FF033E'
: maxVoltage < 2 || maxVoltage > 3.8
: maxVoltage < 3.9 && maxVoltage > 3.8
? '#ffbf00'
: '#32CD32',
color: maxVoltage != 0 ? 'black' : 'white'
@ -495,6 +524,46 @@ function BatteryView(props: BatteryViewProps) {
})()}
</>
)}
{product === 3 && (
<>
{(() => {
const cellVoltagesString =
battery.BatteryDeligreenDataRecord.CellVoltage ||
'';
const cellVoltagesArray: number[] = cellVoltagesString
.split(',')
.map((v) => parseFloat(v.trim()))
.filter((v) => !isNaN(v)); // Filter invalid numbers
const minVoltage = Math.min(...cellVoltagesArray);
const maxVoltage = Math.max(...cellVoltagesArray);
const voltage_diff = maxVoltage - minVoltage;
return (
<>
<TableCell
sx={{
width: '15%',
textAlign: 'center',
padding: '8px',
backgroundColor:
voltage_diff > 0.2
? '#FF033E'
: voltage_diff > 0.1 && voltage_diff < 0.2
? '#ffbf00'
: '#32CD32',
color: voltage_diff != 0 ? 'black' : 'white'
}}
>
{voltage_diff.toFixed(3)} V{' '}
</TableCell>
</>
);
})()}
</>
)}
</TableRow>
))}
</TableBody>

View File

@ -135,6 +135,8 @@ function MainStats(props: MainStatsProps) {
function generateSeries(chartData, category, color) {
const series = [];
const pathsToSearch = [
'Node0',
'Node1',
'Node2',
'Node3',
'Node4',
@ -143,8 +145,7 @@ function MainStats(props: MainStatsProps) {
'Node7',
'Node8',
'Node9',
'Node10',
'Node11'
'Node10'
];
let i = 0;

View File

@ -68,6 +68,113 @@ export interface Device {
TotalBatteryVoltage: number;
};
BatteryDeligreenAlarmRecord: {
OnOffState: {
ChargeSwitchState: string;
CurrentLimitSwitchStat: string;
DischargeSwitchState: string;
HeatingSwitchState: string;
};
AlarmEvent1: {
CellVoltageDropoutFault: string;
ChargeSwitchFault: string;
CurrentLimitSwitchFault: string;
CurrentSensorFault: string;
DischargeSwitchFault: string;
KeySwitchFault: string;
TemperatureSensorFault: string;
VoltageSensorFault: string;
};
AlarmEvent2: {
HighVoltageAlarmForTotalVoltage: string;
LowVoltageAlarmForTotalVoltage: string;
MonomerHighVoltageAlarm: string;
MonomerLowVoltageAlarm: string;
MonomerOvervoltageProtection: string;
MonomerUnderVoltageProtection: string;
OvervoltageProtectionForTotalVoltage: string;
UnderVoltageProtectionForTotalVoltage: string;
};
AlarmEvent3: {
ChargeHighTemperatureAlarm: string;
ChargeLowTemperatureAlarm: string;
ChargeOverTemperatureProtection: string;
ChargeUnderTemperatureProtection: string;
DischargeHighTemperatureAlarm: string;
DischargeLowTemperatureAlarm: string;
DischargeOverTemperatureProtection: string;
DischargeUnderTemperatureProtection: string;
};
AlarmEvent4: {
CellLowTemperatureHeating: string;
EnvironmentHighTemperatureAlarm: string;
EnvironmentLowTemperatureAlarm: string;
EnvironmentOverTemperatureProtection: string;
EnvironmentUnderTemperatureProtection: string;
PowerHighTemperatureAlarm: string;
PowerOverTemperatureProtection: string;
};
AlarmEvent5: {
ChargeOverCurrentAlarm: string;
ChargeOverCurrentProtection: string;
DischargeOverCurrentAlarm: string;
DischargeOverCurrentProtection: string;
OutputShortCircuitLockout: string;
OutputShortCircuitProtection: string;
TransientOverCurrentLockout: string;
TransientOverCurrentProtection: string;
};
AlarmEvent6: {
CellLowVoltageChargingProhibition: string;
ChargeHighVoltageProtection: string;
InsideBit: string;
IntermittentRechargeWaiting: string;
OutputConnectionFault: string;
OutputReversePolarityProtection: string;
ResidualCapacityAlarm: string;
ResidualCapacityProtection: string;
};
AlarmEvent7: {
AutomaticChargingWaiting: string;
InsideBit1: string;
InsideBit2: string;
InsideBit3: string;
InsideBit4: string;
InsideBit5: string;
InsideBit6: string;
ManualChargingWaiting: string;
};
AlarmEvent8: {
CurrentCalibrationNotPerformed: string;
EepStorageFault: string;
InsideBit1: string;
InsideBit2: string;
InsideBit3: string;
RtcError: string;
VoltageCalibrationNotPerformed: string;
ZeroCalibrationNotPerformed: string;
};
CellAlarmList: string;
CellTemperatureAlarm: string;
SystemState: {
Charge: string;
Discharge: string;
FloatingCharge: string;
Shutdown: string;
Standby: string;
};
};
Leds: Leds;
Eoc: boolean;
Soc: number;

View File

@ -159,7 +159,7 @@ export const transformInputToBatteryViewDataJson = async (
);
const adjustedTimestamp =
product == 0
product == 0 || product == 3
? new Date(timestampArray[i] * 1000)
: new Date(timestampArray[i] * 100000);
//Timezone offset is negative, so we convert the timestamp to the current zone by subtracting the corresponding offset
@ -185,19 +185,21 @@ export const transformInputToBatteryViewDataJson = async (
const battery_nodes =
result.Config.Devices.BatteryNodes.toString().split(',');
// console.log(battery_nodes);
//Initialize the chartData structure based on the node names extracted from the first result
let old_length = pathsToSave.length;
if (battery_nodes.length > old_length) {
battery_nodes.forEach((node) => {
if (!pathsToSave.includes('Node' + node)) {
pathsToSave.push('Node' + node);
const node_number =
product == 3 ? Number(node) + 1 : Number(node) - 1;
if (!pathsToSave.includes('Node' + node_number)) {
pathsToSave.push('Node' + node_number);
}
});
}
// console.log(pathsToSave);
if (initialiation) {
initialiation = false;
categories.forEach((category) => {