├── images └── influx.png ├── .gitignore ├── RS485 ├── Screenshot at time of data dump.jpg └── RS485 data.log ├── CAN2JSON ├── CANInformation │ ├── Full Charge Floating On.jpg │ ├── Can bus analysis.txt │ ├── Frame_IDs.md │ ├── Deye Battery Bank.xml │ └── Deye slim.xml ├── appsettings.Development.json ├── can2json.service ├── uninstall.sh ├── Controllers │ ├── Rs485Controller.cs │ └── CandataController.cs ├── install.sh ├── Data │ ├── Context │ │ └── CAN2JSONContext.cs │ ├── Repository │ │ ├── IRepository.cs │ │ └── Repository.cs │ ├── Measurements │ │ ├── BatteryMeasurement.cs │ │ ├── BmsMeasurement.cs │ │ └── BatteryCellMeasurement.cs │ ├── Models │ │ ├── BatteryReading.cs │ │ ├── CellReading.cs │ │ └── BmsReading.cs │ └── Logic │ │ ├── BatteryLogic.cs │ │ ├── BatteryCellLogic.cs │ │ └── BmsLogic.cs ├── appsettings.json ├── Properties │ └── launchSettings.json ├── Interfaces │ ├── IBMSLogic.cs │ ├── IBatteryLogic.cs │ └── IBatteryCellLogic.cs ├── Migrations │ ├── 20230719163820_Int_Date.cs │ ├── 20230719185456_Remove_String_date.cs │ ├── 20230806054840_Add_BatteryCellReading_Model.cs │ ├── 20230714065345_InitialCreate.cs │ ├── 20230714065345_InitialCreate.Designer.cs │ ├── 20230714213902_DataTypes.Designer.cs │ ├── 20230719185456_Remove_String_date.Designer.cs │ ├── 20230719163820_Int_Date.Designer.cs │ ├── Can2JsonContextModelSnapshot.cs │ ├── 20230806054840_Add_BatteryCellReading_Model.Designer.cs │ └── 20230714213902_DataTypes.cs ├── BMS │ ├── CANMON3000.cs │ ├── CANFrame.cs │ ├── Battery.cs │ ├── BatteryManagementSystem.cs │ └── XmlConvert.cs ├── Program.cs ├── CAN2JSON.csproj └── BackgroundServices │ ├── SqliteDbBackgroundService.cs │ ├── InfluxDbBackgroundService.cs │ ├── RS485BackgroundService.cs │ └── SerialDataBackgroundService.cs ├── .github └── workflows │ └── dotnet.yml └── README.md /images/influx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psynosaur/Deye_Battery_Serial_BUS/HEAD/images/influx.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/* 2 | /CAN2JSON/.idea/* 3 | /CAN2JSON/bin/* 4 | /CAN2JSON/obj/* 5 | /CAN2JSON/bmsstats.sqlite 6 | /CAN2JSON/publish/* -------------------------------------------------------------------------------- /RS485/Screenshot at time of data dump.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psynosaur/Deye_Battery_Serial_BUS/HEAD/RS485/Screenshot at time of data dump.jpg -------------------------------------------------------------------------------- /CAN2JSON/CANInformation/Full Charge Floating On.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psynosaur/Deye_Battery_Serial_BUS/HEAD/CAN2JSON/CANInformation/Full Charge Floating On.jpg -------------------------------------------------------------------------------- /CAN2JSON/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CAN2JSON/can2json.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Deye_BMS 3 | After=syslog.target 4 | 5 | [Service] 6 | WorkingDirectory=placeholder 7 | ExecStart=/root/.dotnet/dotnet placeholder/CAN2JSON.dll 8 | Restart=always 9 | RestartSec=30 10 | User=suchuser 11 | SyslogIdentifier=deye_bms_can2json 12 | 13 | [Install] 14 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /CAN2JSON/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Require sudo 4 | if [ $EUID != 0 ]; then 5 | sudo "$0" "$@" 6 | exit $? 7 | fi 8 | 9 | echo "removing service..." 10 | systemctl stop can2json 11 | systemctl disable can2json 12 | echo "done" 13 | 14 | echo "removing service from /etc/systemd/system/..." 15 | rm /etc/systemd/system/can2json.service 16 | echo "done" 17 | 18 | echo "reloading services" 19 | systemctl daemon-reload 20 | echo "done" 21 | 22 | echo "Deye BMS Reader uninstalled successfully!" 23 | echo "Huzzah" -------------------------------------------------------------------------------- /CAN2JSON/CANInformation/Can bus analysis.txt: -------------------------------------------------------------------------------- 1 | ** Always the same 2 | ID Frame ID bytes little endian 3 | DB Data bytes little endian 4 | DS Data size in bytes 5 | ?? Unknown 6 | 7 | ** ** ** ** ** ID ID ** ** DS DB DB DB DB DB DB DB DB ** ?? 8 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 9 | ----------------------------------------------------------- 10 | AA-55-01-01-01-56-03-00-00-08-96-14-8B-00-82-00-00-00-00-1B - 0356 Windows 11 | 12 | AA-55-01-01-01-56-03-00-00-08-14-14-F3-02-AA-00-00-00-00-2B - 0356 Unix 13 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v4 21 | with: 22 | dotnet-version: 8.0.x 23 | - name: Publish 24 | run: dotnet publish ./CAN2JSON/CAN2JSON.csproj -c release 25 | -------------------------------------------------------------------------------- /CAN2JSON/Controllers/Rs485Controller.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace CAN2JSON.Controllers; 5 | 6 | [ApiController] 7 | [Route("[controller]")] 8 | public class Rs485Controller : ControllerBase 9 | { 10 | private readonly ILogger _logger; 11 | private readonly ApplicationInstance _application; 12 | 13 | public Rs485Controller(ILogger logger, ApplicationInstance application) 14 | { 15 | _application = application; 16 | _logger = logger; 17 | } 18 | 19 | [HttpGet(Name = "GetBatteryCells")] 20 | public JsonObject? Get() 21 | { 22 | return _application.Application["jsonSerial"] as JsonObject; 23 | } 24 | } -------------------------------------------------------------------------------- /CAN2JSON/Controllers/CandataController.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace CAN2JSON.Controllers; 5 | 6 | [ApiController] 7 | [Route("[controller]")] 8 | public class CandataController : ControllerBase 9 | { 10 | private readonly ILogger _logger; 11 | private readonly ApplicationInstance _application; 12 | 13 | public CandataController(ILogger logger, ApplicationInstance application) 14 | { 15 | _application = application; 16 | _logger = logger; 17 | } 18 | 19 | [HttpGet(Name = "GetBatteryData")] 20 | public JsonObject? Get() 21 | { 22 | return _application.Application["json"] as JsonObject; 23 | } 24 | } -------------------------------------------------------------------------------- /CAN2JSON/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Require sudo 4 | if [ $EUID != 0 ]; then 5 | sudo "$0" "$@" 6 | exit $? 7 | fi 8 | 9 | pwd=${pwd} 10 | sed -i "s?placeholder?$PWD?g" can2json.service 11 | sed -i "s?suchuser?$SUDO_USER?g" can2json.service 12 | 13 | echo "adding service to /lib/systemd/system/..." 14 | cp can2json.service /etc/systemd/system/ 15 | chmod 644 /etc/systemd/system/can2json.service 16 | echo "done" 17 | 18 | echo "starting and enabling service..." 19 | systemctl daemon-reload 20 | systemctl enable can2json 21 | systemctl start can2json 22 | echo "done" 23 | 24 | echo "Deye BMS Reader installed successfully!" 25 | echo "" 26 | echo "log output can be viewed by running" 27 | echo "sudo journalctl -u can2json.service -f -n" -------------------------------------------------------------------------------- /CAN2JSON/Data/Context/CAN2JSONContext.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.BMS; 2 | using CAN2JSON.Data.Models; 3 | using Microsoft.AspNetCore.Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace CAN2JSON.Data.Context; 7 | 8 | public partial class Can2JsonContext : DbContext 9 | { 10 | public Can2JsonContext() 11 | { 12 | } 13 | 14 | public Can2JsonContext(DbContextOptions options) 15 | : base(options) 16 | { 17 | } 18 | 19 | public virtual DbSet BmsReadings { get; set; } = null!; 20 | public virtual DbSet BatteryReadings { get; set; } = null!; 21 | public virtual DbSet BatteryCellReadings { get; set; } = null!; 22 | 23 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 24 | } -------------------------------------------------------------------------------- /CAN2JSON/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "UseSqLite": false, 10 | "ConnectionStrings": { 11 | "BMSStats": "Data source=./bmsstats.sqlite" 12 | }, 13 | "CANDevice": { 14 | "Path" : "/dev/ttyUSB0" 15 | }, 16 | "InfluxDb": { 17 | "Bucket" : "bmsReadings", 18 | "CellVoltageBucket" : "cellVoltages", 19 | "Org" : "serialdata", 20 | "Token" : "influx_db_token", 21 | "Url" : "http://SomeRemoteAddress:8086" 22 | }, 23 | "BatterySerial": { 24 | "RS485" : true, 25 | "Battery1" : "COM5", 26 | "Battery2" : "COM9", 27 | "Interval" : 2000 28 | }, 29 | "Kestrel": { 30 | "EndPoints": { 31 | "Http": { 32 | "Url": "http://0.0.0.0:5035" 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CAN2JSON/Data/Repository/IRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CAN2JSON.Data.Repository; 2 | 3 | public interface IRepository where TEntity : class 4 | { 5 | /// 6 | /// Get all entities as IQueryable 7 | /// 8 | /// 9 | IQueryable GetAll(); 10 | 11 | /// 12 | /// Adds an entity 13 | /// 14 | /// 15 | /// 16 | Task AddAsync(TEntity entity); 17 | 18 | /// 19 | /// Updates and entity 20 | /// 21 | /// 22 | /// 23 | Task UpdateAsync(TEntity entity); 24 | 25 | /// 26 | /// Deletes an entity 27 | /// 28 | /// 29 | /// 30 | Task DeleteAsync(TEntity entity); 31 | } -------------------------------------------------------------------------------- /CAN2JSON/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://0.0.0.0:45540", 8 | "sslPort": 44306 9 | } 10 | }, 11 | "profiles": { 12 | "CAN2JSON": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://192.168.1.133:7199;http://0.0.0.0:5035", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CAN2JSON/Interfaces/IBMSLogic.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Models; 2 | 3 | namespace CAN2JSON.Interfaces; 4 | 5 | public interface IBmsLogic 6 | { 7 | /// 8 | /// Add a BmsReading 9 | /// 10 | /// 11 | /// 12 | Task AddBmsReading(BmsReading bmsReading); 13 | 14 | /// 15 | /// Get a BmsReading by id 16 | /// 17 | /// 18 | /// 19 | Task GetBmsReadingById(int id); 20 | 21 | /// 22 | /// Update a BmsReading 23 | /// 24 | /// 25 | /// 26 | Task UpdateBmsReading(BmsReading bmsReading); 27 | 28 | /// 29 | /// Delete a BmsReading by id 30 | /// 31 | /// 32 | /// 33 | Task DeleteBmsReading(int id); 34 | } -------------------------------------------------------------------------------- /CAN2JSON/Interfaces/IBatteryLogic.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Models; 2 | 3 | namespace CAN2JSON.Interfaces; 4 | 5 | public interface IBatteryLogic 6 | { 7 | /// 8 | /// Add an BatteryReading 9 | /// 10 | /// 11 | /// 12 | Task AddBatteryReading(BatteryReading batteryReading); 13 | 14 | /// 15 | /// Get an BatteryReading by id 16 | /// 17 | /// 18 | /// 19 | Task GetBatteryReadingById(int id); 20 | 21 | /// 22 | /// Update an BatteryReading 23 | /// 24 | /// 25 | /// 26 | Task UpdateBatteryReading(BatteryReading batteryReading); 27 | 28 | /// 29 | /// Delete an BatteryReading by id 30 | /// 31 | /// 32 | /// 33 | Task DeleteBatteryReading(int id); 34 | } -------------------------------------------------------------------------------- /CAN2JSON/Interfaces/IBatteryCellLogic.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Models; 2 | 3 | namespace CAN2JSON.Interfaces; 4 | 5 | public interface IBatteryCellLogic 6 | { 7 | /// 8 | /// Add an BatteryCellReading 9 | /// 10 | /// 11 | /// 12 | Task AddBatteryCellReading(BatteryCellReading batteryReading); 13 | 14 | /// 15 | /// Get an BatteryCellReading by id 16 | /// 17 | /// 18 | /// 19 | Task GetBatteryCellReadingById(int id); 20 | 21 | /// 22 | /// Update an BatteryCellReading 23 | /// 24 | /// 25 | /// 26 | Task UpdateBatteryCellReading(BatteryCellReading batteryReading); 27 | 28 | /// 29 | /// Delete an BatteryCellReading by id 30 | /// 31 | /// 32 | /// 33 | Task DeleteBatteryCellReading(int id); 34 | } -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230719163820_Int_Date.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace CAN2JSON.Migrations 6 | { 7 | /// 8 | public partial class Int_Date : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Date", 15 | table: "BmsReadings", 16 | type: "INTEGER", 17 | nullable: false, 18 | defaultValue: 0); 19 | 20 | migrationBuilder.AddColumn( 21 | name: "Date", 22 | table: "BatteryReadings", 23 | type: "INTEGER", 24 | nullable: false, 25 | defaultValue: 0); 26 | } 27 | 28 | /// 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropColumn( 32 | name: "Date", 33 | table: "BmsReadings"); 34 | 35 | migrationBuilder.DropColumn( 36 | name: "Date", 37 | table: "BatteryReadings"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230719185456_Remove_String_date.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace CAN2JSON.Migrations 7 | { 8 | /// 9 | public partial class Remove_String_date : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.DropColumn( 15 | name: "DateTime", 16 | table: "BmsReadings"); 17 | 18 | migrationBuilder.DropColumn( 19 | name: "DateTime", 20 | table: "BatteryReadings"); 21 | } 22 | 23 | /// 24 | protected override void Down(MigrationBuilder migrationBuilder) 25 | { 26 | migrationBuilder.AddColumn( 27 | name: "DateTime", 28 | table: "BmsReadings", 29 | type: "TEXT", 30 | nullable: false, 31 | defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); 32 | 33 | migrationBuilder.AddColumn( 34 | name: "DateTime", 35 | table: "BatteryReadings", 36 | type: "TEXT", 37 | nullable: false, 38 | defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CAN2JSON/Data/Measurements/BatteryMeasurement.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using InfluxDB.Client.Core; 3 | 4 | namespace CAN2JSON.Data.Measurements; 5 | [Measurement("BatteryReading")] 6 | public class BatteryMeasurement 7 | { 8 | [Column(IsTimestamp = true)] public int Date { get; set; } 9 | [Column("SlaveNumber", IsTag = true)] public int SlaveNumber { get; set; } 10 | [Column("BatteryVoltage")] public decimal BatteryVoltage { get; set; } 11 | [Column("BatteryCurrent")] public decimal BatteryCurrent { get; set; } 12 | [Column("StateOfCharge")] public decimal StateOfCharge { get; set; } 13 | [Column("StateOfHealth")] public decimal StateOfHealth { get; set; } 14 | [Column("CellVoltageHigh")] public decimal CellVoltageHigh { get; set; } 15 | [Column("CellVoltageLow")] public decimal CellVoltageLow { get; set; } 16 | [Column("CellVoltageDelta")] public decimal CellVoltageDelta { get; set; } 17 | [Column("TemperatureOne")] public decimal TemperatureOne { get; set; } 18 | [Column("TemperatureTwo")] public decimal TemperatureTwo { get; set; } 19 | [Column("TemperatureMos")] public decimal TemperatureMos { get; set; } 20 | [Column("CurrentLimit")] public decimal CurrentLimit { get; set; } 21 | [Column("CurrentLimitMax")] public decimal CurrentLimitMax { get; set; } 22 | [Column("ChargedTotal")] public decimal ChargedTotal { get; set; } 23 | [Column("DischargedTotal")] public decimal DischargedTotal { get; set; } 24 | public decimal Cycles { get; set; } 25 | } -------------------------------------------------------------------------------- /CAN2JSON/BMS/CANMON3000.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Nodes; 3 | using System.Xml; 4 | 5 | namespace CAN2JSON.BMS; 6 | 7 | public class Canmon3000 8 | { 9 | public Canmon3000(string xmlTemplate) 10 | { 11 | XmlTemplate = xmlTemplate; 12 | } 13 | 14 | public string XmlTemplate { get; set; } 15 | 16 | public JsonNode? XmlJnode { get; set; } 17 | 18 | // TODO: loop through members of xml from CAN Monitor 3000 and create BMS attributes programmatically 19 | 20 | public static string ConvertXmlToJson(string xmlContent) 21 | { 22 | XmlDocument xmlDoc = new XmlDocument(); 23 | xmlDoc.LoadXml(xmlContent); 24 | 25 | string json = JsonSerializer.Serialize(xmlDoc.DocumentElement, new JsonSerializerOptions 26 | { 27 | WriteIndented = true 28 | }); 29 | 30 | return json; 31 | } 32 | 33 | public void ParseMessageTreeNodes() 34 | { 35 | XmlJnode = JsonNode.Parse(XmlConvert.XmlToJSON(XmlTemplate)); 36 | } 37 | 38 | public void FindJsonObjectByPropertyName() 39 | { 40 | if (XmlJnode?.AsObject() is { } temp) 41 | foreach (var jn in temp) 42 | { 43 | if (jn.Key.Equals("HeaderTreeNode")) Console.WriteLine($"{jn.Key} {jn.Value}"); 44 | } 45 | } 46 | 47 | public JsonObject ToJson() 48 | { 49 | // TODO: Do something here with CAN template 50 | var json = new JsonObject(); 51 | ParseMessageTreeNodes(); 52 | FindJsonObjectByPropertyName(); 53 | json["XmlTemplate"] = XmlJnode; 54 | return json; 55 | } 56 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Models/BatteryReading.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace CAN2JSON.Data.Models; 6 | 7 | public class BatteryReading 8 | { 9 | [Key] public int Id { get; set; } 10 | public int Date { get; set; } 11 | public int SlaveNumber { get; set; } 12 | [Column(TypeName = "decimal(3, 1)")] public decimal BatteryVoltage { get; set; } 13 | [Column(TypeName = "decimal(3, 1)")] public decimal BatteryCurrent { get; set; } 14 | [Column(TypeName = "decimal(3, 1)")] public decimal StateOfCharge { get; set; } 15 | [Column(TypeName = "decimal(3, 1)")] public decimal StateOfHealth { get; set; } 16 | [Column(TypeName = "decimal(4, 3)")] public decimal CellVoltageHigh { get; set; } 17 | [Column(TypeName = "decimal(4, 3)")] public decimal CellVoltageLow { get; set; } 18 | [Column(TypeName = "decimal(4, 3)")] public decimal CellVoltageDelta { get; set; } 19 | [Column(TypeName = "decimal(3, 1)")] public decimal TemperatureOne { get; set; } 20 | [Column(TypeName = "decimal(3, 1)")] public decimal TemperatureTwo { get; set; } 21 | [Column(TypeName = "decimal(3, 1)")] public decimal TemperatureMos { get; set; } 22 | [Column(TypeName = "decimal(3, 1)")] public decimal CurrentLimit { get; set; } 23 | [Column(TypeName = "decimal(3, 1)")] public decimal CurrentLimitMax { get; set; } 24 | [Column(TypeName = "decimal(7, 3)")] public decimal ChargedTotal { get; set; } 25 | [Column(TypeName = "decimal(7, 3)")] public decimal DischargedTotal { get; set; } 26 | [Column(TypeName = "decimal(4, 3)")] public decimal Cycles { get; set; } 27 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Models/CellReading.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace CAN2JSON.Data.Models; 6 | 7 | public class BatteryCellReading 8 | { 9 | [Key] public int Id { get; set; } 10 | public int Date { get; set; } 11 | public int SlaveNumber { get; set; } 12 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell01 { get; set; } 13 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell02 { get; set; } 14 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell03 { get; set; } 15 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell04 { get; set; } 16 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell05 { get; set; } 17 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell06 { get; set; } 18 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell07 { get; set; } 19 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell08 { get; set; } 20 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell09 { get; set; } 21 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell10 { get; set; } 22 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell11 { get; set; } 23 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell12 { get; set; } 24 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell13 { get; set; } 25 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell14 { get; set; } 26 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell15 { get; set; } 27 | [Column(TypeName = "decimal(4, 3)")] public decimal Cell16 { get; set; } 28 | [Column(TypeName = "decimal(0, 0)")] public decimal MinPos { get; set; } 29 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Logic/BatteryLogic.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Models; 2 | using CAN2JSON.Data.Repository; 3 | using CAN2JSON.Interfaces; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace CAN2JSON.Data.Logic; 7 | 8 | public class BatteryLogic : IBatteryLogic 9 | { 10 | private readonly IRepository _repository; 11 | 12 | public BatteryLogic(IRepository repository) => _repository = repository; 13 | 14 | public async Task GetBatteryReadingById(int id) 15 | { 16 | if (id == 0) 17 | throw new Exception($"BatteryReading {nameof(id)} needs to be provided"); 18 | 19 | BatteryReading batteryReading = await _repository.GetAll() 20 | .FirstOrDefaultAsync(br => br.Id == id) ?? throw new Exception($"{nameof(BatteryReading)} with {nameof(id)} not found");; 21 | 22 | return batteryReading; 23 | } 24 | 25 | public async Task AddBatteryReading(BatteryReading batteryReading) => await _repository.AddAsync(batteryReading); 26 | 27 | public async Task UpdateBatteryReading(BatteryReading batteryReading) 28 | { 29 | return await _repository.UpdateAsync(batteryReading); 30 | } 31 | 32 | public async Task DeleteBatteryReading(int id) 33 | { 34 | if (id == 0) 35 | throw new Exception($"BatteryReading {nameof(id)} needs to be provided"); 36 | 37 | BatteryReading? batteryReading = await _repository.GetAll().FirstOrDefaultAsync(br => br.Id == id); 38 | 39 | if(batteryReading == null) 40 | throw new Exception($"BatteryReading does not exist"); 41 | 42 | return await _repository.DeleteAsync(batteryReading); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Measurements/BmsMeasurement.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using InfluxDB.Client.Core; 3 | 4 | namespace CAN2JSON.Data.Measurements; 5 | [Measurement("BmsReading")] 6 | public class BmsMeasurement 7 | { 8 | [Key] public int Id { get; set; } 9 | [Column(IsTimestamp = true)] public int Date { get; set; } 10 | [Column("ChargeCurrentLimit")] public decimal ChargeCurrentLimit { get; set; } 11 | [Column("ChargeCurrentLimitMax")] public decimal ChargeCurrentLimitMax { get; set; } 12 | [Column("Voltage")] public decimal Voltage { get; set; } 13 | [Column("Amps")] public decimal Amps { get; set; } 14 | [Column("Watts")] public decimal Watts { get; set; } 15 | [Column("Temperature")] public decimal Temperature { get; set; } 16 | [Column("StateOfCharge")] public decimal StateOfCharge { get; set; } 17 | [Column("StateOfHealth")] public decimal StateOfHealth { get; set; } 18 | [Column("CellVoltageHigh")] public decimal CellVoltageHigh { get; set; } 19 | [Column("CellVoltageLow")] public decimal CellVoltageLow { get; set; } 20 | [Column("CellVoltageDelta")] public decimal CellVoltageDelta { get; set; } 21 | [Column("BmsTemperatureHigh")] public decimal BmsTemperatureHigh { get; set; } 22 | [Column("BmsTemperatureLow")] public decimal BmsTemperatureLow { get; set; } 23 | [Column("BatteryCapacity")] public decimal BatteryCapacity { get; set; } 24 | [Column("ChargeVoltage")] public decimal ChargeVoltage { get; set; } 25 | [Column("CurrentLimit")] public decimal CurrentLimit { get; set; } 26 | [Column("DischargeLimit")] public decimal DischargeLimit { get; set; } 27 | [Column("BatteryCutoffVoltage")] public decimal BatteryCutoffVoltage { get; set; } 28 | [Column("FullChargedRestingVoltage")] public decimal FullChargedRestingVoltage { get; set; } 29 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Logic/BatteryCellLogic.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Models; 2 | using CAN2JSON.Data.Repository; 3 | using CAN2JSON.Interfaces; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace CAN2JSON.Data.Logic; 7 | 8 | public class BatteryCellLogic : IBatteryCellLogic 9 | { 10 | private readonly IRepository _repository; 11 | 12 | public BatteryCellLogic(IRepository repository) => _repository = repository; 13 | 14 | public async Task GetBatteryCellReadingById(int id) 15 | { 16 | if (id == 0) 17 | throw new Exception($"BatteryCellReading {nameof(id)} needs to be provided"); 18 | 19 | BatteryCellReading batteryReading = await _repository.GetAll() 20 | .FirstOrDefaultAsync(br => br.Id == id) ?? throw new Exception($"{nameof(BatteryCellReading)} with {nameof(id)} not found");; 21 | 22 | return batteryReading; 23 | } 24 | 25 | public async Task AddBatteryCellReading(BatteryCellReading batteryReading) => await _repository.AddAsync(batteryReading); 26 | 27 | public async Task UpdateBatteryCellReading(BatteryCellReading batteryReading) 28 | { 29 | return await _repository.UpdateAsync(batteryReading); 30 | } 31 | 32 | public async Task DeleteBatteryCellReading(int id) 33 | { 34 | if (id == 0) 35 | throw new Exception($"BatteryCellReading {nameof(id)} needs to be provided"); 36 | 37 | BatteryCellReading? batteryReading = await _repository.GetAll().FirstOrDefaultAsync(br => br.Id == id); 38 | 39 | if(batteryReading == null) 40 | throw new Exception($"BatteryCellReading does not exist"); 41 | 42 | return await _repository.DeleteAsync(batteryReading); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Logic/BmsLogic.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Context; 2 | using CAN2JSON.Data.Models; 3 | using CAN2JSON.Data.Repository; 4 | using CAN2JSON.Interfaces; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace CAN2JSON.Data.Logic; 8 | 9 | public class BmsLogic : IBmsLogic 10 | { 11 | private readonly IRepository _repository; 12 | 13 | public BmsLogic( 14 | IRepository repository) 15 | { 16 | _repository = repository; 17 | } 18 | 19 | public async Task GetBmsReadingById(int id) 20 | { 21 | if (id == 0) 22 | throw new Exception($"Book {nameof(id)} needs to be provided"); 23 | 24 | BmsReading bookModel = await _repository.GetAll() 25 | .Include(b => b.BatteryReadings) 26 | .FirstOrDefaultAsync(bms => bms.Id == id) 27 | ?? throw new Exception($"{nameof(BmsReading)} with {nameof(id)} not found"); 28 | 29 | return bookModel; 30 | } 31 | 32 | public async Task AddBmsReading(BmsReading bmsReading) => await _repository.AddAsync(bmsReading); 33 | 34 | 35 | public async Task UpdateBmsReading(BmsReading bmsReading) => await _repository.UpdateAsync(bmsReading); 36 | 37 | public async Task DeleteBmsReading(int id) 38 | { 39 | if (id == 0) 40 | throw new Exception($"BmsReading {nameof(id)} needs to be provided"); 41 | 42 | 43 | BmsReading? book = await _repository.GetAll() 44 | .Include(b => b.BatteryReadings) 45 | .FirstOrDefaultAsync(b => b.Id == id); 46 | 47 | if (book == null) 48 | throw new Exception($"BmsReading does not exist"); 49 | await _repository.DeleteAsync(book); 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Deye battery serial bus reader 2 | 3 | ### Data 4 | - CAN data frames 5 | - Full array information 6 | - RS485 individual cell voltages 7 | - **Will move to seperate project soon** 8 | 9 | ### Software prerequisites 10 | - InfluxDB on some remote server 11 | - Setup orginisation and bucket and replace values in `appsetting.json` 12 | 13 | ![influx.png](./images/influx.png) 14 | 15 | 16 | ### Dashboard API's 17 | https://xxx.xxx.xxx.xxx:5035/candata 18 | https://xxx.xxx.xxx.xxx:5035/rs485 19 | 20 | ### Grafana dashboard (file included) 21 | ![image](https://github.com/Psynosaur/Deye_Battery_Serial_BUS/assets/26934113/9ae661c9-07a9-4ad3-8502-b58bbeacd145) 22 | 23 | 24 | ### Was built from findings with [CAN Monitor 3000](https://github.com/tixiv/CAN-Monitor-qt) 25 | 26 | Includes CAN BUS tree (XML) of known frame types 27 | 28 | #### CAN Monitor 3000 Windows application 29 | 30 | ![image](https://github.com/Psynosaur/Deye_Battery_CAN_BUS/assets/26934113/fcef5139-bc05-49d1-b6c4-5e910b7498f6) 31 | 32 | #### Based on this device: 33 | 34 | ![image](https://github.com/Psynosaur/Deye_Battery_CAN_BUS/assets/26934113/04c1c34b-6d6d-4141-acb8-f41646d75c32) 35 | 36 | #### Hardware connection side 37 | ![image](https://github.com/Psynosaur/Deye_Battery_CAN_BUS/assets/26934113/905d121c-b3f2-4eac-9578-d9c0cf407022) 38 | 39 | 40 | ![image](https://github.com/Psynosaur/Deye_Battery_CAN_BUS/assets/26934113/86d262d1-b2b0-4e49-8af9-236f0f778b98) 41 | 42 | ## TODO 43 | - Add support for InterCAN-BUS messages 44 | - Cell voltages 45 | - 6x Temp sensors 46 | - Should require only **one** CAN device for all information 47 | - Add setup details for raspberry pi 48 | - CAN frames 49 | - Debugging for Frame analysis 50 | - Split RS485 and CAN into seperate projects 51 | - 52 | - Document reverse engineering process in more detail 53 | - RS485 serial payload 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /CAN2JSON/Data/Models/BmsReading.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace CAN2JSON.Data.Models; 6 | 7 | public class BmsReading 8 | { 9 | [Key] public int Id { get; set; } 10 | public int Date { get; set; } 11 | [Column(TypeName = "decimal(3, 0)")] public decimal ChargeCurrentLimit { get; set; } 12 | [Column(TypeName = "decimal(3, 0)")] public decimal ChargeCurrentLimitMax { get; set; } 13 | [Column(TypeName = "decimal(3, 1)")] public decimal Voltage { get; set; } 14 | [Column(TypeName = "decimal(3, 1)")] public decimal Amps { get; set; } 15 | [Column(TypeName = "decimal(5, 1)")] public decimal Watts { get; set; } 16 | [Column(TypeName = "decimal(3, 1)")] public decimal Temperature { get; set; } 17 | [Column(TypeName = "decimal(3, 1)")] public decimal StateOfCharge { get; set; } 18 | [Column(TypeName = "decimal(3, 1)")] public decimal StateOfHealth { get; set; } 19 | [Column(TypeName = "decimal(4, 3)")] public decimal CellVoltageHigh { get; set; } 20 | [Column(TypeName = "decimal(4, 3)")] public decimal CellVoltageLow { get; set; } 21 | [Column(TypeName = "decimal(4, 3)")] public decimal CellVoltageDelta { get; set; } 22 | [Column(TypeName = "decimal(3, 1)")] public decimal BmsTemperatureHigh { get; set; } 23 | [Column(TypeName = "decimal(3, 1)")] public decimal BmsTemperatureLow { get; set; } 24 | [Column(TypeName = "decimal(3, 1)")] public decimal BatteryCapacity { get; set; } 25 | [Column(TypeName = "decimal(3, 1)")] public decimal ChargeVoltage { get; set; } 26 | [Column(TypeName = "decimal(3, 1)")] public decimal CurrentLimit { get; set; } 27 | [Column(TypeName = "decimal(3, 1)")] public decimal DischargeLimit { get; set; } 28 | [Column(TypeName = "decimal(3, 1)")] public decimal BatteryCutoffVoltage { get; set; } 29 | [Column(TypeName = "decimal(3, 1)")] public decimal FullChargedRestingVoltage { get; set; } 30 | 31 | public List? BatteryReadings { get; set; } 32 | } -------------------------------------------------------------------------------- /CAN2JSON/Data/Measurements/BatteryCellMeasurement.cs: -------------------------------------------------------------------------------- 1 | using InfluxDB.Client.Core; 2 | using RestSharp; 3 | 4 | namespace CAN2JSON.Data.Measurements; 5 | [Measurement("BatteryCellReading")] 6 | public class BatteryCellMeasurement 7 | { 8 | [Column(IsTimestamp = true)] public int Date { get; set; } 9 | [Column("BatteryNumber", IsTag = true)] public int SlaveNumber { get; set; } 10 | [Column("Cell01")] public decimal Cell01 { get; set; } 11 | [Column("Cell02")] public decimal Cell02 { get; set; } 12 | [Column("Cell03")] public decimal Cell03 { get; set; } 13 | [Column("Cell04")] public decimal Cell04 { get; set; } 14 | [Column("Cell05")] public decimal Cell05 { get; set; } 15 | [Column("Cell06")] public decimal Cell06 { get; set; } 16 | [Column("Cell07")] public decimal Cell07 { get; set; } 17 | [Column("Cell08")] public decimal Cell08 { get; set; } 18 | [Column("Cell09")] public decimal Cell09 { get; set; } 19 | [Column("Cell10")] public decimal Cell10 { get; set; } 20 | [Column("Cell11")] public decimal Cell11 { get; set; } 21 | [Column("Cell12")] public decimal Cell12 { get; set; } 22 | [Column("Cell13")] public decimal Cell13 { get; set; } 23 | [Column("Cell14")] public decimal Cell14 { get; set; } 24 | [Column("Cell15")] public decimal Cell15 { get; set; } 25 | [Column("Cell16")] public decimal Cell16 { get; set; } 26 | [Column("MinPos")] public decimal MinPos { get; set; } 27 | 28 | public JsonObject ToJson() 29 | { 30 | var json = new JsonObject(); 31 | json["MinPos"] = MinPos; 32 | json["Cell01"] = Cell01; 33 | json["Cell02"] = Cell02; 34 | json["Cell03"] = Cell03; 35 | json["Cell04"] = Cell04; 36 | json["Cell05"] = Cell05; 37 | json["Cell06"] = Cell06; 38 | json["Cell07"] = Cell07; 39 | json["Cell08"] = Cell08; 40 | json["Cell09"] = Cell09; 41 | json["Cell10"] = Cell10; 42 | json["Cell11"] = Cell11; 43 | json["Cell12"] = Cell12; 44 | json["Cell13"] = Cell13; 45 | json["Cell14"] = Cell14; 46 | json["Cell15"] = Cell15; 47 | json["Cell16"] = Cell16; 48 | return json; 49 | } 50 | } -------------------------------------------------------------------------------- /CAN2JSON/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Text.Json.Nodes; 3 | using CAN2JSON; 4 | using CAN2JSON.BackgroundServices; 5 | using CAN2JSON.Data.Context; 6 | using CAN2JSON.Data.Logic; 7 | using CAN2JSON.Data.Repository; 8 | using CAN2JSON.Interfaces; 9 | using Microsoft.EntityFrameworkCore; 10 | 11 | 12 | var builder = WebApplication.CreateBuilder(args); 13 | // Add services to the container. 14 | 15 | builder.Services.AddControllers(); 16 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 17 | builder.Services.AddEndpointsApiExplorer(); 18 | var sqlite = builder.Configuration.GetValue("UseSqLite"); 19 | if (sqlite is "True") 20 | { 21 | builder.Services.AddDbContext 22 | (options => options.UseSqlite("Name=BMSStats")); 23 | builder.Services.AddHostedService(); 24 | /* These are for when we'd like to do more CRUD related actions with our battery readings... 25 | * builder.Services.AddScoped(); 26 | * builder.Services.AddScoped(); 27 | */ 28 | } 29 | 30 | 31 | // Background workers 32 | builder.Services.AddHostedService(); 33 | builder.Services.AddHostedService(); 34 | 35 | // Optional RS485 36 | var batteryRs485 = builder.Configuration.GetValue("BatterySerial:RS485"); 37 | if(batteryRs485 is "True") builder.Services.AddHostedService(); 38 | builder.Services.AddSwaggerGen(); 39 | 40 | 41 | 42 | // This is our concurrent dictionary for accessing our latest serial readings from our workers 43 | builder.Services.AddSingleton(); 44 | 45 | builder.Services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); 46 | 47 | 48 | var app = builder.Build(); 49 | 50 | // Configure the HTTP request pipeline. 51 | if (app.Environment.IsDevelopment()) 52 | { 53 | app.UseSwagger(); 54 | app.UseSwaggerUI(); 55 | } 56 | 57 | app.UseHttpsRedirection(); 58 | 59 | // app.UseAuthorization(); 60 | 61 | app.MapControllers(); 62 | 63 | app.Run(); 64 | 65 | public class ApplicationInstance { 66 | 67 | public ConcurrentDictionary Application { get; } = new(); 68 | } -------------------------------------------------------------------------------- /CAN2JSON/CAN2JSON.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | default 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | PreserveNewest 33 | 34 | 35 | PreserveNewest 36 | 37 | 38 | Deye Battery Bank.xml 39 | PreserveNewest 40 | 41 | 42 | 43 | Deye slim.xml 44 | PreserveNewest 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /CAN2JSON/Data/Repository/Repository.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.Data.Context; 2 | 3 | namespace CAN2JSON.Data.Repository; 4 | 5 | public class Repository : IRepository where TEntity : class, new() 6 | { 7 | protected readonly Can2JsonContext Can2JsonContext; 8 | 9 | public Repository(Can2JsonContext can2JsonContext) 10 | { 11 | Can2JsonContext = can2JsonContext; 12 | } 13 | 14 | public IQueryable GetAll() 15 | { 16 | try 17 | { 18 | return Can2JsonContext.Set(); 19 | } 20 | catch (Exception ex) 21 | { 22 | throw new Exception($"Couldn't retrieve entities: {ex.Message}"); 23 | } 24 | } 25 | 26 | public async Task AddAsync(TEntity entity) 27 | { 28 | if (entity == null) 29 | throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null"); 30 | 31 | try 32 | { 33 | await Can2JsonContext.AddAsync(entity); 34 | await Can2JsonContext.SaveChangesAsync(); 35 | 36 | return entity; 37 | } 38 | catch (Exception ex) 39 | { 40 | throw new Exception($"{nameof(entity)} could not be saved: {ex.Message}"); 41 | } 42 | } 43 | 44 | public async Task UpdateAsync(TEntity entity) 45 | { 46 | if (entity == null) 47 | throw new ArgumentNullException($"{nameof(UpdateAsync)} entity must not be null"); 48 | 49 | try 50 | { 51 | Can2JsonContext.Update(entity); 52 | await Can2JsonContext.SaveChangesAsync(); 53 | 54 | return entity; 55 | } 56 | catch (Exception ex) 57 | { 58 | throw new Exception($"{nameof(entity)} could not be updated: {ex.Message}"); 59 | } 60 | } 61 | 62 | public async Task DeleteAsync(TEntity entity) 63 | { 64 | if (entity == null) 65 | throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null"); 66 | 67 | try 68 | { 69 | Can2JsonContext.Remove(entity); 70 | await Can2JsonContext.SaveChangesAsync(); 71 | 72 | return entity; 73 | } 74 | catch (Exception ex) 75 | { 76 | throw new Exception($"{nameof(entity)} could not be deleted: {ex.Message}"); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /CAN2JSON/BMS/CANFrame.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text.Json.Nodes; 3 | 4 | namespace CAN2JSON.BMS; 5 | 6 | public class CANFrame 7 | { 8 | public string FrameId { get; protected set; } 9 | public byte[] Data { get; protected set; } 10 | public byte DataSize { get; protected set; } 11 | public byte[][] DataBytes { get; protected set; } 12 | 13 | public string DataHexString { get; protected set; } 14 | 15 | public short[] DataInShorts { get; protected set; } 16 | public int[] DataInInt32 { get; protected set; } 17 | 18 | public DateTime LastUpdated { get; set; } // N 19 | 20 | public CANFrame(string frameId) 21 | { 22 | FrameId = frameId; 23 | DataInShorts = new short[4]; 24 | Data = new byte[20]; 25 | DataBytes = new byte[4][]; 26 | LastUpdated = DateTime.Now; 27 | DataHexString = ""; 28 | DataInInt32 = new int[2]; 29 | } 30 | 31 | public void UpdateValues(byte[] values) 32 | { 33 | var dataOffset = 10; 34 | DataHexString = BitConverter.ToString(values).Replace("-", " "); 35 | if (values.Length == 20) 36 | { 37 | for (int i = 0; i < 20; i++) 38 | { 39 | Data[i] = values[i]; 40 | } 41 | 42 | DataSize = Data[9]; 43 | 44 | for (int i = 0; i < 4; i++) 45 | { 46 | DataBytes[i] = new[] 47 | { 48 | Data[dataOffset], 49 | Data[dataOffset + 1] 50 | }; 51 | DataInShorts[i] = BitConverter.ToInt16(DataBytes[i]); 52 | dataOffset += 2; 53 | 54 | } 55 | 56 | dataOffset = 10; 57 | for (var i = 0; i < 2; i++) 58 | { 59 | var intArr = new[]{ 60 | Data[dataOffset], 61 | Data[dataOffset + 1], 62 | Data[dataOffset + 2], 63 | Data[dataOffset + 3], 64 | }; 65 | DataInInt32[i] = BitConverter.ToInt32(intArr); 66 | dataOffset += 4; 67 | } 68 | } 69 | } 70 | 71 | public JsonObject ToJson() 72 | { 73 | var json = new JsonObject(); 74 | json["FrameId"] = FrameId; 75 | json["Hex"] = DataHexString; 76 | json["DataSize"] = DataSize; 77 | json["Data1"] = DataInShorts[0]; 78 | json["Data2"] = DataInShorts[1]; 79 | json["Data3"] = DataInShorts[2]; 80 | json["Data4"] = DataInShorts[3]; 81 | json["LastUpdated"] = LastUpdated.ToString("yyyy-MM-dd HH:mm:ss"); 82 | return json; 83 | } 84 | } -------------------------------------------------------------------------------- /CAN2JSON/BMS/Battery.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | 3 | namespace CAN2JSON.BMS 4 | { 5 | public class Battery 6 | { 7 | public decimal BatteryVoltage { get; set; } 8 | public decimal BatteryCurrent { get; set; } 9 | public decimal StateOfCharge { get; set; } 10 | public decimal StateOfHealth { get; set; } 11 | 12 | public decimal CellVoltageHigh { get; set; } 13 | public decimal CellVoltageLow { get; set; } 14 | 15 | public decimal CellVoltageDelta { get; set; } 16 | 17 | public decimal TemperatureOne { get; set; } 18 | public decimal TemperatureTwo { get; set; } 19 | public decimal TemperatureMos { get; set; } 20 | 21 | public decimal CurrentLimit { get; set; } 22 | public decimal CurrentLimitMax { get; set; } 23 | 24 | public decimal ChargedTotal { get; set; } 25 | public decimal DischargedTotal { get; set; } 26 | 27 | public decimal Cycles { get; set; } 28 | public BatteryBmsStatuses? Status { get; set; } 29 | 30 | public JsonObject ToJson() 31 | { 32 | var json = new JsonObject(); 33 | json["Voltage"] = BatteryVoltage; 34 | json["Current"] = BatteryCurrent; 35 | json["StateOfCharge"] = StateOfCharge; 36 | json["StateOfHealth"] = StateOfHealth; 37 | json["CellHigh"] = CellVoltageHigh; 38 | json["CellLow"] = CellVoltageLow; 39 | json["CellDelta"] = CellVoltageDelta; 40 | json["Temp1"] = TemperatureOne; 41 | json["Temp2"] = TemperatureTwo; 42 | json["TemperatureMos"] = TemperatureMos; 43 | json["CurrentLimit"] = CurrentLimit; 44 | json["CurrentLimitMax"] = CurrentLimitMax; 45 | json["ChargedTotal"] = ChargedTotal; 46 | json["DischargedTotal"] = DischargedTotal; 47 | json["Cycles"] = Cycles; 48 | json["Status"] = Status is not null ? Status.ToJson() : "" ; 49 | return json; 50 | } 51 | } 52 | } 53 | public class BatteryBmsStatuses 54 | { 55 | public byte Status1 { get; set; } 56 | public byte Status2 { get; set; } 57 | public byte Status3 { get; set; } 58 | public byte Status4 { get; set; } 59 | public byte Status5 { get; set; } 60 | public byte Status6 { get; set; } 61 | public byte Status7 { get; set; } 62 | public byte Status8 { get; set; } 63 | 64 | 65 | public JsonObject ToJson() 66 | { 67 | var json = new JsonObject(); 68 | string bmsStatus = Status1 == 0 ? "Standby" : Status1 == 1 ? "Charge" : "Discharge"; 69 | json["=>"] = $"{bmsStatus}, {Status2}, {Status3} cycles, {Status4}, {Status5}, {Status6}, SubSystem {Status7}, {Status8}"; 70 | return json; 71 | } 72 | } -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230806054840_Add_BatteryCellReading_Model.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace CAN2JSON.Migrations 6 | { 7 | /// 8 | public partial class Add_BatteryCellReading_Model : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.CreateTable( 14 | name: "BatteryCellReadings", 15 | columns: table => new 16 | { 17 | Id = table.Column(type: "INTEGER", nullable: false) 18 | .Annotation("Sqlite:Autoincrement", true), 19 | Date = table.Column(type: "INTEGER", nullable: false), 20 | SlaveNumber = table.Column(type: "INTEGER", nullable: false), 21 | Cell01 = table.Column(type: "decimal(4, 3)", nullable: false), 22 | Cell02 = table.Column(type: "decimal(4, 3)", nullable: false), 23 | Cell03 = table.Column(type: "decimal(4, 3)", nullable: false), 24 | Cell04 = table.Column(type: "decimal(4, 3)", nullable: false), 25 | Cell05 = table.Column(type: "decimal(4, 3)", nullable: false), 26 | Cell06 = table.Column(type: "decimal(4, 3)", nullable: false), 27 | Cell07 = table.Column(type: "decimal(4, 3)", nullable: false), 28 | Cell08 = table.Column(type: "decimal(4, 3)", nullable: false), 29 | Cell09 = table.Column(type: "decimal(4, 3)", nullable: false), 30 | Cell10 = table.Column(type: "decimal(4, 3)", nullable: false), 31 | Cell11 = table.Column(type: "decimal(4, 3)", nullable: false), 32 | Cell12 = table.Column(type: "decimal(4, 3)", nullable: false), 33 | Cell13 = table.Column(type: "decimal(4, 3)", nullable: false), 34 | Cell14 = table.Column(type: "decimal(4, 3)", nullable: false), 35 | Cell15 = table.Column(type: "decimal(4, 3)", nullable: false), 36 | Cell16 = table.Column(type: "decimal(4, 3)", nullable: false), 37 | MinPos = table.Column(type: "decimal(0, 0)", nullable: false) 38 | }, 39 | constraints: table => 40 | { 41 | table.PrimaryKey("PK_BatteryCellReadings", x => x.Id); 42 | }); 43 | } 44 | 45 | /// 46 | protected override void Down(MigrationBuilder migrationBuilder) 47 | { 48 | migrationBuilder.DropTable( 49 | name: "BatteryCellReadings"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /CAN2JSON/CANInformation/Frame_IDs.md: -------------------------------------------------------------------------------- 1 | ### Known frame id's 2 | 3 | | Frame ID | Description | Format of 8 data bytes(signed bits in little endian) | 4 | |----------|----------------------------------------------------------------------|------------------------------------------------------| 5 | | | **CAN Network Devices** | | 6 | | 305 | Inverter Addr/Status | s8,s8,s8,s8,s8,s8,s8,s8 | 7 | | 359 | Battery #1 CAN Status | s8,s8,s8,s8,s8,s8,s8,s8 | 8 | | 35C | Battery #2 CAN Status | s8,s8,s8,s8,s8,s8,s8,s8 | 9 | | | **BMS Properties** | | 10 | | 351 | Chg(V), MaxChgCur(A), MaxDisChgCur(A), CutOff(V) | s16,s16,s16,s16 | 11 | | 355 | SOC, SOH, *, * | s16,s16,s16,s16 | 12 | | 356 | SysVolt(V), SysCurr(A), SysTemp(°C), * | s16,s16,s16,s16 | 13 | | 35E | Model number : DY001 | HEX to ASCII | 14 | | 361 | SysMaxCellVolt(V), SysMinCellVolt(V), SysTempMax(°C), SysTempMin(°C) | s16,s16,s16,s16 | 15 | | 363 | SysCapacity(Ah), FullChgVolt(V), *, * | s16,s16,s16,s16 | 16 | | 364 | Status and SysBattCnt(5th byte in array) | s8,s8,s8,s8,s8,s8,s8,s8 | 17 | | 371 | MaxChgCur(A), MaxDisChgCur(A), *, * | s16,s16,s16,s16 | 18 | | | **Battery Properties** | | 19 | | | **+1 to Frame ID per additional battery** | | 20 | | 110 | Alarms? | s8,s8,s8,s8,s8,s8,s8,s8 | 21 | | 150 | Voltage, Current, SOC, SOH | s16,s16,s16,s16 | 22 | | 200 | Cell Voltage (High, Low), Temp (1, 2) | s16,s16,s16,s16 | 23 | | 250 | MOS Temp, ? , Limits (Current, Max) | s16,s16,s16,s16 | 24 | | 400 | Status: 1;2;0, cycles(2nd byte), *, 15;9;, 3-18 | s8,s8,s8,s8,s8,s8,s8,s8 | 25 | | 500 | Boot version | HEX to ASCII | 26 | | 550 | Power IN and Out | s32,s32 | 27 | | 600 | Serial number first half | HEX to ASCII | 28 | | 650 | Serial number second half | HEX to ASCII | 29 | | 700 | ID 700 | s8,s8,s8,s8,s8,s8,s8,s8 | 30 | | 750 | ID 750 | s8,s8,s8,s8,s8,s8,s8,s8 | 31 | -------------------------------------------------------------------------------- /CAN2JSON/BMS/BatteryManagementSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Nodes; 3 | 4 | namespace CAN2JSON.BMS; 5 | 6 | public class BatteryManagementSystem 7 | { 8 | public decimal ChargeVoltage { get; set; } 9 | public decimal CurrentLimit { get; set; } 10 | public decimal DischargeLimit { get; set; } 11 | public decimal BatteryCutoffVoltage { get; set; } 12 | public decimal ChargeCurrentLimit { get; set; } 13 | public decimal ChargeCurrentLimitMax { get; set; } 14 | public decimal Voltage { get; set; } 15 | public decimal Amps { get; set; } 16 | public decimal Watts { get; set; } 17 | public decimal Temperature { get; set; } 18 | public decimal StateOfCharge { get; set; } 19 | public decimal StateOfHealth { get; set; } 20 | public decimal CellVoltageHigh { get; set; } 21 | public decimal CellVoltageLow { get; set; } 22 | public decimal CellVoltageDelta { get; set; } 23 | public decimal BmsTemperatureHigh { get; set; } 24 | public decimal BmsTemperatureLow { get; set; } 25 | public decimal BatteryCapacity { get; set; } 26 | 27 | public decimal FullChargedRestingVoltage { get; set; } 28 | public string? LastUpdate { get; set; } 29 | public BmsStatuses? Statuses { get; set; } 30 | 31 | public List Batteries { get; set; } 32 | 33 | public List CanFrames { get; set; } 34 | 35 | public BatteryManagementSystem(int numBatteries) 36 | { 37 | Batteries = new List(numBatteries); 38 | CanFrames = new List(); 39 | Statuses = new BmsStatuses(); 40 | } 41 | 42 | public void UpdateBattery(int batteryIndex, Battery battery) 43 | { 44 | CheckBatteryIndex(batteryIndex); 45 | Batteries[batteryIndex] = battery; 46 | } 47 | 48 | private void CheckBatteryIndex(int batteryIndex) 49 | { 50 | if (batteryIndex < 0 || batteryIndex >= Batteries.Count) 51 | throw new ArgumentOutOfRangeException(nameof(batteryIndex), "Invalid battery index."); 52 | } 53 | 54 | public JsonObject ToJson() 55 | { 56 | var json = new JsonObject(); 57 | json["SOC"] = StateOfCharge; 58 | json["SOH"] = StateOfHealth; 59 | json["Voltage"] = Voltage; 60 | json["Amp"] = Amps; 61 | json["Watts"] = Watts; 62 | json["Temp"] = Temperature; 63 | json["Cell(V) H"] = CellVoltageHigh; 64 | json["Cell(V) L"] = CellVoltageLow; 65 | json["Cell(V) d"] = CellVoltageHigh - CellVoltageLow /1m; 66 | json["BmsTempHigh"] = BmsTemperatureHigh; 67 | json["BmsTempLow"] = BmsTemperatureLow; 68 | json["ChargeLimit(A)"] = ChargeCurrentLimit; 69 | json["ChargeLimitMax(A)"] = ChargeCurrentLimitMax; 70 | json["Cut off(V)"] = BatteryCutoffVoltage; 71 | json["Charge(V)"] = ChargeVoltage; 72 | json["CurrentLimit(A)"] = CurrentLimit; 73 | json["DischargeLimit(A)"] = DischargeLimit; 74 | json["BatteryCapacity(Ah)"] = BatteryCapacity; 75 | json["Resting(V)"] = FullChargedRestingVoltage; 76 | json["Statuses"] = Statuses is not null ? Statuses.ToJson() : ""; 77 | var batteriesJsonArray = new JsonArray(); 78 | foreach (var battery in Batteries) 79 | { 80 | var batteryJsonString = battery.ToJson().ToString(); 81 | using var document = JsonDocument.Parse(batteryJsonString); 82 | var batteryJsonObject = document.RootElement.Clone(); 83 | batteriesJsonArray.Add(batteryJsonObject); 84 | } 85 | json["Batteries"] = batteriesJsonArray; 86 | 87 | var canFramesJsonArray = new JsonArray(); 88 | foreach (var frame in CanFrames) 89 | { 90 | var frameJsonString = frame.ToJson().ToString(); 91 | using var document = JsonDocument.Parse(frameJsonString); 92 | var frameJsonObject = document.RootElement.Clone(); 93 | canFramesJsonArray.Add(frameJsonObject); 94 | } 95 | json["CanFrames"] = canFramesJsonArray; 96 | json["date"] = LastUpdate; 97 | 98 | return json; 99 | } 100 | } 101 | 102 | public class BmsStatuses 103 | { 104 | public byte Status1 { get; set; } 105 | public byte Status2 { get; set; } 106 | public byte Status3 { get; set; } 107 | public byte Status4 { get; set; } 108 | public byte Status5 { get; set; } 109 | public byte Status6 { get; set; } 110 | public byte Status7 { get; set; } 111 | public byte Status8 { get; set; } 112 | 113 | 114 | public JsonObject ToJson() 115 | { 116 | var json = new JsonObject(); 117 | json["=>"] = $"{Status1}, {Status2}, {Status3}, {Status4}, {Status5}, {Status6}, {Status7}, {Status8}"; 118 | return json; 119 | } 120 | } -------------------------------------------------------------------------------- /CAN2JSON/CANInformation/Deye Battery Bank.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /CAN2JSON/CANInformation/Deye slim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 11 | 13 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 29 | 31 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 51 | 53 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230714065345_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace CAN2JSON.Migrations 7 | { 8 | /// 9 | public partial class InitialCreate : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "BmsReadings", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "INTEGER", nullable: false) 19 | .Annotation("Sqlite:Autoincrement", true), 20 | DateTime = table.Column(type: "TEXT", nullable: false), 21 | ChargeCurrentLimit = table.Column(type: "TEXT", nullable: false), 22 | ChargeCurrentLimitMax = table.Column(type: "TEXT", nullable: false), 23 | Voltage = table.Column(type: "TEXT", nullable: false), 24 | Amps = table.Column(type: "TEXT", nullable: false), 25 | Watts = table.Column(type: "TEXT", nullable: false), 26 | Temperature = table.Column(type: "TEXT", nullable: false), 27 | StateOfCharge = table.Column(type: "TEXT", nullable: false), 28 | StateOfHealth = table.Column(type: "TEXT", nullable: false), 29 | CellVoltageHigh = table.Column(type: "TEXT", nullable: false), 30 | CellVoltageLow = table.Column(type: "TEXT", nullable: false), 31 | CellVoltageDelta = table.Column(type: "TEXT", nullable: false), 32 | BmsTemperatureHigh = table.Column(type: "TEXT", nullable: false), 33 | BmsTemperatureLow = table.Column(type: "TEXT", nullable: false), 34 | BatteryCapacity = table.Column(type: "TEXT", nullable: false), 35 | ChargeVoltage = table.Column(type: "TEXT", nullable: false), 36 | CurrentLimit = table.Column(type: "TEXT", nullable: false), 37 | DischargeLimit = table.Column(type: "TEXT", nullable: false), 38 | BatteryCutoffVoltage = table.Column(type: "TEXT", nullable: false), 39 | FullChargedRestingVoltage = table.Column(type: "TEXT", nullable: false) 40 | }, 41 | constraints: table => 42 | { 43 | table.PrimaryKey("PK_BmsReadings", x => x.Id); 44 | }); 45 | 46 | migrationBuilder.CreateTable( 47 | name: "BatteryReadings", 48 | columns: table => new 49 | { 50 | Id = table.Column(type: "INTEGER", nullable: false) 51 | .Annotation("Sqlite:Autoincrement", true), 52 | DateTime = table.Column(type: "TEXT", nullable: false), 53 | SlaveNumber = table.Column(type: "INTEGER", nullable: false), 54 | BatteryVoltage = table.Column(type: "TEXT", nullable: false), 55 | BatteryCurrent = table.Column(type: "TEXT", nullable: false), 56 | StateOfCharge = table.Column(type: "TEXT", nullable: false), 57 | StateOfHealth = table.Column(type: "TEXT", nullable: false), 58 | CellVoltageHigh = table.Column(type: "TEXT", nullable: false), 59 | CellVoltageLow = table.Column(type: "TEXT", nullable: false), 60 | CellVoltageDelta = table.Column(type: "TEXT", nullable: false), 61 | TemperatureOne = table.Column(type: "TEXT", nullable: false), 62 | TemperatureTwo = table.Column(type: "TEXT", nullable: false), 63 | TemperatureMos = table.Column(type: "TEXT", nullable: false), 64 | CurrentLimit = table.Column(type: "TEXT", nullable: false), 65 | CurrentLimitMax = table.Column(type: "TEXT", nullable: false), 66 | ChargedTotal = table.Column(type: "TEXT", nullable: false), 67 | DischargedTotal = table.Column(type: "TEXT", nullable: false), 68 | Cycles = table.Column(type: "TEXT", nullable: false), 69 | BmsReadingId = table.Column(type: "INTEGER", nullable: true) 70 | }, 71 | constraints: table => 72 | { 73 | table.PrimaryKey("PK_BatteryReadings", x => x.Id); 74 | table.ForeignKey( 75 | name: "FK_BatteryReadings_BmsReadings_BmsReadingId", 76 | column: x => x.BmsReadingId, 77 | principalTable: "BmsReadings", 78 | principalColumn: "Id"); 79 | }); 80 | 81 | migrationBuilder.CreateIndex( 82 | name: "IX_BatteryReadings_BmsReadingId", 83 | table: "BatteryReadings", 84 | column: "BmsReadingId"); 85 | } 86 | 87 | /// 88 | protected override void Down(MigrationBuilder migrationBuilder) 89 | { 90 | migrationBuilder.DropTable( 91 | name: "BatteryReadings"); 92 | 93 | migrationBuilder.DropTable( 94 | name: "BmsReadings"); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CAN2JSON/BMS/XmlConvert.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Xml; 3 | 4 | namespace CAN2JSON.BMS; 5 | 6 | public static class XmlConvert 7 | { 8 | public static string XmlToJSON(string xml) 9 | { 10 | XmlDocument doc = new XmlDocument(); 11 | doc.LoadXml(xml); 12 | 13 | return XmlToJSON(doc); 14 | } 15 | 16 | public static string XmlToJSON(XmlDocument xmlDoc) 17 | { 18 | StringBuilder sbJSON = new StringBuilder(); 19 | sbJSON.Append("{ "); 20 | XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true); 21 | sbJSON.Append("}"); 22 | return sbJSON.ToString(); 23 | } 24 | 25 | // XmlToJSONnode: Output an XmlElement, possibly as part of a higher array 26 | private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName) 27 | { 28 | if (showNodeName) 29 | sbJSON.Append("\"" + SafeJSON(node.Name) + "\": "); 30 | sbJSON.Append("{"); 31 | // Build a sorted list of key-value pairs 32 | // where key is case-sensitive nodeName 33 | // value is an ArrayList of string or XmlElement 34 | // so that we know whether the nodeName is an array or not. 35 | SortedList childNodeNames = new SortedList(); 36 | 37 | // Add in all node attributes 38 | if (node.Attributes != null) 39 | foreach (XmlAttribute attr in node.Attributes) 40 | StoreChildNode(childNodeNames, attr.Name, attr.InnerText); 41 | 42 | // Add in all nodes 43 | foreach (XmlNode cnode in node.ChildNodes) 44 | { 45 | if (cnode is XmlText) 46 | StoreChildNode(childNodeNames, "value", cnode.InnerText); 47 | else if (cnode is XmlElement) 48 | StoreChildNode(childNodeNames, cnode.Name, cnode); 49 | } 50 | 51 | // Now output all stored info 52 | foreach (string childname in childNodeNames.Keys) 53 | { 54 | List alChild = (List)childNodeNames[childname]; 55 | if (alChild.Count == 1) 56 | OutputNode(childname, alChild[0], sbJSON, true); 57 | else 58 | { 59 | sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ "); 60 | foreach (object Child in alChild) 61 | OutputNode(childname, Child, sbJSON, false); 62 | sbJSON.Remove(sbJSON.Length - 2, 2); 63 | sbJSON.Append(" ], "); 64 | } 65 | } 66 | 67 | sbJSON.Remove(sbJSON.Length - 2, 2); 68 | sbJSON.Append(" }"); 69 | } 70 | 71 | // StoreChildNode: Store data associated with each nodeName 72 | // so that we know whether the nodeName is an array or not. 73 | private static void StoreChildNode(SortedList childNodeNames, string nodeName, object nodeValue) 74 | { 75 | // Pre-process contraction of XmlElement-s 76 | if (nodeValue is XmlElement) 77 | { 78 | // Convert into "aa":null 79 | // xx into "aa":"xx" 80 | XmlNode cnode = (XmlNode)nodeValue; 81 | if (cnode.Attributes.Count == 0) 82 | { 83 | XmlNodeList children = cnode.ChildNodes; 84 | if (children.Count == 0) 85 | nodeValue = null; 86 | else if (children.Count == 1 && (children[0] is XmlText)) 87 | nodeValue = ((XmlText)(children[0])).InnerText; 88 | } 89 | } 90 | 91 | // Add nodeValue to ArrayList associated with each nodeName 92 | // If nodeName doesn't exist then add it 93 | List ValuesAL; 94 | 95 | if (childNodeNames.ContainsKey(nodeName)) 96 | { 97 | ValuesAL = (List)childNodeNames[nodeName]; 98 | } 99 | else 100 | { 101 | ValuesAL = new List(); 102 | childNodeNames[nodeName] = ValuesAL; 103 | } 104 | 105 | ValuesAL.Add(nodeValue); 106 | } 107 | 108 | private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName) 109 | { 110 | if (alChild == null) 111 | { 112 | if (showNodeName) 113 | sbJSON.Append("\"" + SafeJSON(childname) + "\": "); 114 | sbJSON.Append("null"); 115 | } 116 | else if (alChild is string) 117 | { 118 | if (showNodeName) 119 | sbJSON.Append("\"" + SafeJSON(childname) + "\": "); 120 | string sChild = (string)alChild; 121 | sChild = sChild.Trim(); 122 | sbJSON.Append("\"" + SafeJSON(sChild) + "\""); 123 | } 124 | else 125 | XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName); 126 | 127 | sbJSON.Append(", "); 128 | } 129 | 130 | // Make a string safe for JSON 131 | private static string SafeJSON(string sIn) 132 | { 133 | StringBuilder sbOut = new StringBuilder(sIn.Length); 134 | foreach (char ch in sIn) 135 | { 136 | if (Char.IsControl(ch) || ch == '\'') 137 | { 138 | int ich = (int)ch; 139 | sbOut.Append(@"\u" + ich.ToString("x4")); 140 | continue; 141 | } 142 | else if (ch == '\"' || ch == '\\' || ch == '/') 143 | { 144 | sbOut.Append('\\'); 145 | } 146 | 147 | sbOut.Append(ch); 148 | } 149 | 150 | return sbOut.ToString(); 151 | } 152 | } -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230714065345_InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using CAN2JSON.Data.Context; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace CAN2JSON.Migrations 12 | { 13 | [DbContext(typeof(Can2JsonContext))] 14 | [Migration("20230714065345_InitialCreate")] 15 | partial class InitialCreate 16 | { 17 | /// 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); 22 | 23 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd() 27 | .HasColumnType("INTEGER"); 28 | 29 | b.Property("BatteryCurrent") 30 | .HasColumnType("TEXT"); 31 | 32 | b.Property("BatteryVoltage") 33 | .HasColumnType("TEXT"); 34 | 35 | b.Property("BmsReadingId") 36 | .HasColumnType("INTEGER"); 37 | 38 | b.Property("CellVoltageDelta") 39 | .HasColumnType("TEXT"); 40 | 41 | b.Property("CellVoltageHigh") 42 | .HasColumnType("TEXT"); 43 | 44 | b.Property("CellVoltageLow") 45 | .HasColumnType("TEXT"); 46 | 47 | b.Property("ChargedTotal") 48 | .HasColumnType("TEXT"); 49 | 50 | b.Property("CurrentLimit") 51 | .HasColumnType("TEXT"); 52 | 53 | b.Property("CurrentLimitMax") 54 | .HasColumnType("TEXT"); 55 | 56 | b.Property("Cycles") 57 | .HasColumnType("TEXT"); 58 | 59 | b.Property("DateTime") 60 | .HasColumnType("TEXT"); 61 | 62 | b.Property("DischargedTotal") 63 | .HasColumnType("TEXT"); 64 | 65 | b.Property("SlaveNumber") 66 | .HasColumnType("INTEGER"); 67 | 68 | b.Property("StateOfCharge") 69 | .HasColumnType("TEXT"); 70 | 71 | b.Property("StateOfHealth") 72 | .HasColumnType("TEXT"); 73 | 74 | b.Property("TemperatureMos") 75 | .HasColumnType("TEXT"); 76 | 77 | b.Property("TemperatureOne") 78 | .HasColumnType("TEXT"); 79 | 80 | b.Property("TemperatureTwo") 81 | .HasColumnType("TEXT"); 82 | 83 | b.HasKey("Id"); 84 | 85 | b.HasIndex("BmsReadingId"); 86 | 87 | b.ToTable("BatteryReadings"); 88 | }); 89 | 90 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 91 | { 92 | b.Property("Id") 93 | .ValueGeneratedOnAdd() 94 | .HasColumnType("INTEGER"); 95 | 96 | b.Property("Amps") 97 | .HasColumnType("TEXT"); 98 | 99 | b.Property("BatteryCapacity") 100 | .HasColumnType("TEXT"); 101 | 102 | b.Property("BatteryCutoffVoltage") 103 | .HasColumnType("TEXT"); 104 | 105 | b.Property("BmsTemperatureHigh") 106 | .HasColumnType("TEXT"); 107 | 108 | b.Property("BmsTemperatureLow") 109 | .HasColumnType("TEXT"); 110 | 111 | b.Property("CellVoltageDelta") 112 | .HasColumnType("TEXT"); 113 | 114 | b.Property("CellVoltageHigh") 115 | .HasColumnType("TEXT"); 116 | 117 | b.Property("CellVoltageLow") 118 | .HasColumnType("TEXT"); 119 | 120 | b.Property("ChargeCurrentLimit") 121 | .HasColumnType("TEXT"); 122 | 123 | b.Property("ChargeCurrentLimitMax") 124 | .HasColumnType("TEXT"); 125 | 126 | b.Property("ChargeVoltage") 127 | .HasColumnType("TEXT"); 128 | 129 | b.Property("CurrentLimit") 130 | .HasColumnType("TEXT"); 131 | 132 | b.Property("DateTime") 133 | .HasColumnType("TEXT"); 134 | 135 | b.Property("DischargeLimit") 136 | .HasColumnType("TEXT"); 137 | 138 | b.Property("FullChargedRestingVoltage") 139 | .HasColumnType("TEXT"); 140 | 141 | b.Property("StateOfCharge") 142 | .HasColumnType("TEXT"); 143 | 144 | b.Property("StateOfHealth") 145 | .HasColumnType("TEXT"); 146 | 147 | b.Property("Temperature") 148 | .HasColumnType("TEXT"); 149 | 150 | b.Property("Voltage") 151 | .HasColumnType("TEXT"); 152 | 153 | b.Property("Watts") 154 | .HasColumnType("TEXT"); 155 | 156 | b.HasKey("Id"); 157 | 158 | b.ToTable("BmsReadings"); 159 | }); 160 | 161 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 162 | { 163 | b.HasOne("CAN2JSON.Data.Models.BmsReading", null) 164 | .WithMany("BatteryReadings") 165 | .HasForeignKey("BmsReadingId"); 166 | }); 167 | 168 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 169 | { 170 | b.Navigation("BatteryReadings"); 171 | }); 172 | #pragma warning restore 612, 618 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230714213902_DataTypes.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using CAN2JSON.Data.Context; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace CAN2JSON.Migrations 12 | { 13 | [DbContext(typeof(Can2JsonContext))] 14 | [Migration("20230714213902_DataTypes")] 15 | partial class DataTypes 16 | { 17 | /// 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); 22 | 23 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd() 27 | .HasColumnType("INTEGER"); 28 | 29 | b.Property("BatteryCurrent") 30 | .HasColumnType("decimal(3, 1)"); 31 | 32 | b.Property("BatteryVoltage") 33 | .HasColumnType("decimal(3, 1)"); 34 | 35 | b.Property("BmsReadingId") 36 | .HasColumnType("INTEGER"); 37 | 38 | b.Property("CellVoltageDelta") 39 | .HasColumnType("decimal(4, 3)"); 40 | 41 | b.Property("CellVoltageHigh") 42 | .HasColumnType("decimal(4, 3)"); 43 | 44 | b.Property("CellVoltageLow") 45 | .HasColumnType("decimal(4, 3)"); 46 | 47 | b.Property("ChargedTotal") 48 | .HasColumnType("decimal(7, 3)"); 49 | 50 | b.Property("CurrentLimit") 51 | .HasColumnType("decimal(3, 1)"); 52 | 53 | b.Property("CurrentLimitMax") 54 | .HasColumnType("decimal(3, 1)"); 55 | 56 | b.Property("Cycles") 57 | .HasColumnType("decimal(4, 3)"); 58 | 59 | b.Property("DateTime") 60 | .HasColumnType("TEXT"); 61 | 62 | b.Property("DischargedTotal") 63 | .HasColumnType("decimal(7, 3)"); 64 | 65 | b.Property("SlaveNumber") 66 | .HasColumnType("INTEGER"); 67 | 68 | b.Property("StateOfCharge") 69 | .HasColumnType("decimal(3, 1)"); 70 | 71 | b.Property("StateOfHealth") 72 | .HasColumnType("decimal(3, 1)"); 73 | 74 | b.Property("TemperatureMos") 75 | .HasColumnType("decimal(3, 1)"); 76 | 77 | b.Property("TemperatureOne") 78 | .HasColumnType("decimal(3, 1)"); 79 | 80 | b.Property("TemperatureTwo") 81 | .HasColumnType("decimal(3, 1)"); 82 | 83 | b.HasKey("Id"); 84 | 85 | b.HasIndex("BmsReadingId"); 86 | 87 | b.ToTable("BatteryReadings"); 88 | }); 89 | 90 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 91 | { 92 | b.Property("Id") 93 | .ValueGeneratedOnAdd() 94 | .HasColumnType("INTEGER"); 95 | 96 | b.Property("Amps") 97 | .HasColumnType("decimal(3, 1)"); 98 | 99 | b.Property("BatteryCapacity") 100 | .HasColumnType("decimal(3, 1)"); 101 | 102 | b.Property("BatteryCutoffVoltage") 103 | .HasColumnType("decimal(3, 1)"); 104 | 105 | b.Property("BmsTemperatureHigh") 106 | .HasColumnType("decimal(3, 1)"); 107 | 108 | b.Property("BmsTemperatureLow") 109 | .HasColumnType("decimal(3, 1)"); 110 | 111 | b.Property("CellVoltageDelta") 112 | .HasColumnType("decimal(4, 3)"); 113 | 114 | b.Property("CellVoltageHigh") 115 | .HasColumnType("decimal(4, 3)"); 116 | 117 | b.Property("CellVoltageLow") 118 | .HasColumnType("decimal(4, 3)"); 119 | 120 | b.Property("ChargeCurrentLimit") 121 | .HasColumnType("decimal(3, 0)"); 122 | 123 | b.Property("ChargeCurrentLimitMax") 124 | .HasColumnType("decimal(3, 0)"); 125 | 126 | b.Property("ChargeVoltage") 127 | .HasColumnType("decimal(3, 1)"); 128 | 129 | b.Property("CurrentLimit") 130 | .HasColumnType("decimal(3, 1)"); 131 | 132 | b.Property("DateTime") 133 | .HasColumnType("TEXT"); 134 | 135 | b.Property("DischargeLimit") 136 | .HasColumnType("decimal(3, 1)"); 137 | 138 | b.Property("FullChargedRestingVoltage") 139 | .HasColumnType("decimal(3, 1)"); 140 | 141 | b.Property("StateOfCharge") 142 | .HasColumnType("decimal(3, 1)"); 143 | 144 | b.Property("StateOfHealth") 145 | .HasColumnType("decimal(3, 1)"); 146 | 147 | b.Property("Temperature") 148 | .HasColumnType("decimal(3, 1)"); 149 | 150 | b.Property("Voltage") 151 | .HasColumnType("decimal(3, 1)"); 152 | 153 | b.Property("Watts") 154 | .HasColumnType("decimal(5, 1)"); 155 | 156 | b.HasKey("Id"); 157 | 158 | b.ToTable("BmsReadings"); 159 | }); 160 | 161 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 162 | { 163 | b.HasOne("CAN2JSON.Data.Models.BmsReading", null) 164 | .WithMany("BatteryReadings") 165 | .HasForeignKey("BmsReadingId"); 166 | }); 167 | 168 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 169 | { 170 | b.Navigation("BatteryReadings"); 171 | }); 172 | #pragma warning restore 612, 618 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230719185456_Remove_String_date.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using CAN2JSON.Data.Context; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace CAN2JSON.Migrations 12 | { 13 | [DbContext(typeof(Can2JsonContext))] 14 | [Migration("20230719185456_Remove_String_date")] 15 | partial class Remove_String_date 16 | { 17 | /// 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); 22 | 23 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd() 27 | .HasColumnType("INTEGER"); 28 | 29 | b.Property("BatteryCurrent") 30 | .HasColumnType("decimal(3, 1)"); 31 | 32 | b.Property("BatteryVoltage") 33 | .HasColumnType("decimal(3, 1)"); 34 | 35 | b.Property("BmsReadingId") 36 | .HasColumnType("INTEGER"); 37 | 38 | b.Property("CellVoltageDelta") 39 | .HasColumnType("decimal(4, 3)"); 40 | 41 | b.Property("CellVoltageHigh") 42 | .HasColumnType("decimal(4, 3)"); 43 | 44 | b.Property("CellVoltageLow") 45 | .HasColumnType("decimal(4, 3)"); 46 | 47 | b.Property("ChargedTotal") 48 | .HasColumnType("decimal(7, 3)"); 49 | 50 | b.Property("CurrentLimit") 51 | .HasColumnType("decimal(3, 1)"); 52 | 53 | b.Property("CurrentLimitMax") 54 | .HasColumnType("decimal(3, 1)"); 55 | 56 | b.Property("Cycles") 57 | .HasColumnType("decimal(4, 3)"); 58 | 59 | b.Property("Date") 60 | .HasColumnType("INTEGER"); 61 | 62 | b.Property("DischargedTotal") 63 | .HasColumnType("decimal(7, 3)"); 64 | 65 | b.Property("SlaveNumber") 66 | .HasColumnType("INTEGER"); 67 | 68 | b.Property("StateOfCharge") 69 | .HasColumnType("decimal(3, 1)"); 70 | 71 | b.Property("StateOfHealth") 72 | .HasColumnType("decimal(3, 1)"); 73 | 74 | b.Property("TemperatureMos") 75 | .HasColumnType("decimal(3, 1)"); 76 | 77 | b.Property("TemperatureOne") 78 | .HasColumnType("decimal(3, 1)"); 79 | 80 | b.Property("TemperatureTwo") 81 | .HasColumnType("decimal(3, 1)"); 82 | 83 | b.HasKey("Id"); 84 | 85 | b.HasIndex("BmsReadingId"); 86 | 87 | b.ToTable("BatteryReadings"); 88 | }); 89 | 90 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 91 | { 92 | b.Property("Id") 93 | .ValueGeneratedOnAdd() 94 | .HasColumnType("INTEGER"); 95 | 96 | b.Property("Amps") 97 | .HasColumnType("decimal(3, 1)"); 98 | 99 | b.Property("BatteryCapacity") 100 | .HasColumnType("decimal(3, 1)"); 101 | 102 | b.Property("BatteryCutoffVoltage") 103 | .HasColumnType("decimal(3, 1)"); 104 | 105 | b.Property("BmsTemperatureHigh") 106 | .HasColumnType("decimal(3, 1)"); 107 | 108 | b.Property("BmsTemperatureLow") 109 | .HasColumnType("decimal(3, 1)"); 110 | 111 | b.Property("CellVoltageDelta") 112 | .HasColumnType("decimal(4, 3)"); 113 | 114 | b.Property("CellVoltageHigh") 115 | .HasColumnType("decimal(4, 3)"); 116 | 117 | b.Property("CellVoltageLow") 118 | .HasColumnType("decimal(4, 3)"); 119 | 120 | b.Property("ChargeCurrentLimit") 121 | .HasColumnType("decimal(3, 0)"); 122 | 123 | b.Property("ChargeCurrentLimitMax") 124 | .HasColumnType("decimal(3, 0)"); 125 | 126 | b.Property("ChargeVoltage") 127 | .HasColumnType("decimal(3, 1)"); 128 | 129 | b.Property("CurrentLimit") 130 | .HasColumnType("decimal(3, 1)"); 131 | 132 | b.Property("Date") 133 | .HasColumnType("INTEGER"); 134 | 135 | b.Property("DischargeLimit") 136 | .HasColumnType("decimal(3, 1)"); 137 | 138 | b.Property("FullChargedRestingVoltage") 139 | .HasColumnType("decimal(3, 1)"); 140 | 141 | b.Property("StateOfCharge") 142 | .HasColumnType("decimal(3, 1)"); 143 | 144 | b.Property("StateOfHealth") 145 | .HasColumnType("decimal(3, 1)"); 146 | 147 | b.Property("Temperature") 148 | .HasColumnType("decimal(3, 1)"); 149 | 150 | b.Property("Voltage") 151 | .HasColumnType("decimal(3, 1)"); 152 | 153 | b.Property("Watts") 154 | .HasColumnType("decimal(5, 1)"); 155 | 156 | b.HasKey("Id"); 157 | 158 | b.ToTable("BmsReadings"); 159 | }); 160 | 161 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 162 | { 163 | b.HasOne("CAN2JSON.Data.Models.BmsReading", null) 164 | .WithMany("BatteryReadings") 165 | .HasForeignKey("BmsReadingId"); 166 | }); 167 | 168 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 169 | { 170 | b.Navigation("BatteryReadings"); 171 | }); 172 | #pragma warning restore 612, 618 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230719163820_Int_Date.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using CAN2JSON.Data.Context; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace CAN2JSON.Migrations 12 | { 13 | [DbContext(typeof(Can2JsonContext))] 14 | [Migration("20230719163820_Int_Date")] 15 | partial class Int_Date 16 | { 17 | /// 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); 22 | 23 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd() 27 | .HasColumnType("INTEGER"); 28 | 29 | b.Property("BatteryCurrent") 30 | .HasColumnType("decimal(3, 1)"); 31 | 32 | b.Property("BatteryVoltage") 33 | .HasColumnType("decimal(3, 1)"); 34 | 35 | b.Property("BmsReadingId") 36 | .HasColumnType("INTEGER"); 37 | 38 | b.Property("CellVoltageDelta") 39 | .HasColumnType("decimal(4, 3)"); 40 | 41 | b.Property("CellVoltageHigh") 42 | .HasColumnType("decimal(4, 3)"); 43 | 44 | b.Property("CellVoltageLow") 45 | .HasColumnType("decimal(4, 3)"); 46 | 47 | b.Property("ChargedTotal") 48 | .HasColumnType("decimal(7, 3)"); 49 | 50 | b.Property("CurrentLimit") 51 | .HasColumnType("decimal(3, 1)"); 52 | 53 | b.Property("CurrentLimitMax") 54 | .HasColumnType("decimal(3, 1)"); 55 | 56 | b.Property("Cycles") 57 | .HasColumnType("decimal(4, 3)"); 58 | 59 | b.Property("Date") 60 | .HasColumnType("INTEGER"); 61 | 62 | b.Property("DateTime") 63 | .HasColumnType("TEXT"); 64 | 65 | b.Property("DischargedTotal") 66 | .HasColumnType("decimal(7, 3)"); 67 | 68 | b.Property("SlaveNumber") 69 | .HasColumnType("INTEGER"); 70 | 71 | b.Property("StateOfCharge") 72 | .HasColumnType("decimal(3, 1)"); 73 | 74 | b.Property("StateOfHealth") 75 | .HasColumnType("decimal(3, 1)"); 76 | 77 | b.Property("TemperatureMos") 78 | .HasColumnType("decimal(3, 1)"); 79 | 80 | b.Property("TemperatureOne") 81 | .HasColumnType("decimal(3, 1)"); 82 | 83 | b.Property("TemperatureTwo") 84 | .HasColumnType("decimal(3, 1)"); 85 | 86 | b.HasKey("Id"); 87 | 88 | b.HasIndex("BmsReadingId"); 89 | 90 | b.ToTable("BatteryReadings"); 91 | }); 92 | 93 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 94 | { 95 | b.Property("Id") 96 | .ValueGeneratedOnAdd() 97 | .HasColumnType("INTEGER"); 98 | 99 | b.Property("Amps") 100 | .HasColumnType("decimal(3, 1)"); 101 | 102 | b.Property("BatteryCapacity") 103 | .HasColumnType("decimal(3, 1)"); 104 | 105 | b.Property("BatteryCutoffVoltage") 106 | .HasColumnType("decimal(3, 1)"); 107 | 108 | b.Property("BmsTemperatureHigh") 109 | .HasColumnType("decimal(3, 1)"); 110 | 111 | b.Property("BmsTemperatureLow") 112 | .HasColumnType("decimal(3, 1)"); 113 | 114 | b.Property("CellVoltageDelta") 115 | .HasColumnType("decimal(4, 3)"); 116 | 117 | b.Property("CellVoltageHigh") 118 | .HasColumnType("decimal(4, 3)"); 119 | 120 | b.Property("CellVoltageLow") 121 | .HasColumnType("decimal(4, 3)"); 122 | 123 | b.Property("ChargeCurrentLimit") 124 | .HasColumnType("decimal(3, 0)"); 125 | 126 | b.Property("ChargeCurrentLimitMax") 127 | .HasColumnType("decimal(3, 0)"); 128 | 129 | b.Property("ChargeVoltage") 130 | .HasColumnType("decimal(3, 1)"); 131 | 132 | b.Property("CurrentLimit") 133 | .HasColumnType("decimal(3, 1)"); 134 | 135 | b.Property("Date") 136 | .HasColumnType("INTEGER"); 137 | 138 | b.Property("DateTime") 139 | .HasColumnType("TEXT"); 140 | 141 | b.Property("DischargeLimit") 142 | .HasColumnType("decimal(3, 1)"); 143 | 144 | b.Property("FullChargedRestingVoltage") 145 | .HasColumnType("decimal(3, 1)"); 146 | 147 | b.Property("StateOfCharge") 148 | .HasColumnType("decimal(3, 1)"); 149 | 150 | b.Property("StateOfHealth") 151 | .HasColumnType("decimal(3, 1)"); 152 | 153 | b.Property("Temperature") 154 | .HasColumnType("decimal(3, 1)"); 155 | 156 | b.Property("Voltage") 157 | .HasColumnType("decimal(3, 1)"); 158 | 159 | b.Property("Watts") 160 | .HasColumnType("decimal(5, 1)"); 161 | 162 | b.HasKey("Id"); 163 | 164 | b.ToTable("BmsReadings"); 165 | }); 166 | 167 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 168 | { 169 | b.HasOne("CAN2JSON.Data.Models.BmsReading", null) 170 | .WithMany("BatteryReadings") 171 | .HasForeignKey("BmsReadingId"); 172 | }); 173 | 174 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 175 | { 176 | b.Navigation("BatteryReadings"); 177 | }); 178 | #pragma warning restore 612, 618 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /CAN2JSON/BackgroundServices/SqliteDbBackgroundService.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.BMS; 2 | using CAN2JSON.Data.Measurements; 3 | using CAN2JSON.Data.Models; 4 | using CAN2JSON.Data.Repository; 5 | using InfluxDB.Client; 6 | using InfluxDB.Client.Api.Domain; 7 | using Task = System.Threading.Tasks.Task; 8 | 9 | namespace CAN2JSON.BackgroundServices; 10 | 11 | public class SqliteDbBackgroundService : BackgroundService 12 | { 13 | private readonly ApplicationInstance _application; 14 | private readonly ILogger _logger; 15 | private IServiceProvider Services { get; } 16 | 17 | public SqliteDbBackgroundService(IServiceProvider services, ILogger logger, 18 | ApplicationInstance application) 19 | { 20 | _logger = logger; 21 | _application = application; 22 | Services = services; 23 | } 24 | 25 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 26 | { 27 | while (!stoppingToken.IsCancellationRequested) 28 | { 29 | await PerformFunction(stoppingToken); 30 | 31 | await Task.Delay(2000, stoppingToken); 32 | } 33 | } 34 | 35 | private async Task PerformFunction(CancellationToken stoppingToken) 36 | { 37 | try 38 | { 39 | if (_application.Application["bms"] is BatteryManagementSystem bms) 40 | { 41 | var tbms = bms; 42 | var batteryReads = tbms.Batteries.Select((battery, index) => new BatteryReading 43 | { 44 | BatteryVoltage = battery.BatteryVoltage, 45 | BatteryCurrent = battery.BatteryCurrent, 46 | StateOfCharge = battery.StateOfCharge, 47 | StateOfHealth = battery.StateOfHealth, 48 | CellVoltageHigh = battery.CellVoltageHigh, 49 | CellVoltageLow = battery.CellVoltageLow, 50 | CellVoltageDelta = battery.CellVoltageDelta, 51 | TemperatureOne = battery.TemperatureOne, 52 | TemperatureTwo = battery.TemperatureTwo, 53 | TemperatureMos = battery.TemperatureMos, 54 | CurrentLimit = battery.CurrentLimit, 55 | CurrentLimitMax = battery.CurrentLimitMax, 56 | ChargedTotal = battery.ChargedTotal, 57 | DischargedTotal = battery.DischargedTotal, 58 | Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds, 59 | SlaveNumber = index 60 | }) 61 | .ToList(); 62 | if (batteryReads.Any(br => 63 | br.DischargedTotal == 0 || 64 | br.ChargedTotal == 0 || 65 | br.TemperatureOne == 0 || 66 | br.TemperatureTwo == 0 || 67 | br.TemperatureMos == 0 68 | )) return; 69 | 70 | if (tbms.Voltage == 0) return; 71 | if (batteryReads.Count != 2) return; 72 | 73 | var bmsReads = new BmsReading() 74 | { 75 | Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds, 76 | ChargeCurrentLimit = tbms.ChargeCurrentLimit, 77 | ChargeCurrentLimitMax = tbms.ChargeCurrentLimitMax, 78 | Voltage = tbms.Voltage, 79 | Amps = tbms.Amps, 80 | Watts = tbms.Watts, 81 | Temperature = tbms.Temperature, 82 | StateOfCharge = tbms.StateOfCharge, 83 | StateOfHealth = tbms.StateOfHealth, 84 | CellVoltageHigh = tbms.CellVoltageHigh, 85 | CellVoltageLow = tbms.CellVoltageLow, 86 | CellVoltageDelta = tbms.CellVoltageDelta, 87 | BmsTemperatureHigh = tbms.BmsTemperatureHigh, 88 | BmsTemperatureLow = tbms.BmsTemperatureLow, 89 | BatteryCapacity = tbms.BatteryCapacity, 90 | ChargeVoltage = tbms.ChargeVoltage, 91 | CurrentLimit = tbms.CurrentLimit, 92 | DischargeLimit = tbms.DischargeLimit, 93 | BatteryCutoffVoltage = tbms.BatteryCutoffVoltage, 94 | FullChargedRestingVoltage = tbms.FullChargedRestingVoltage, 95 | BatteryReadings = batteryReads 96 | }; 97 | 98 | //SQLite 99 | using var scope = Services.CreateScope(); 100 | var scopedProcessingService = 101 | scope.ServiceProvider 102 | .GetRequiredService>(); 103 | 104 | await scopedProcessingService.AddAsync(bmsReads); 105 | } 106 | 107 | if (_application.Application["rs485"] is List cellReadings) 108 | { 109 | var creads = cellReadings; 110 | var batteryReads = creads.Select((battery, index) => new BatteryCellReading 111 | { 112 | Cell01 = battery.Cell01, 113 | Cell02 = battery.Cell02, 114 | Cell03 = battery.Cell03, 115 | Cell04 = battery.Cell04, 116 | Cell05 = battery.Cell05, 117 | Cell06 = battery.Cell06, 118 | Cell07 = battery.Cell07, 119 | Cell08 = battery.Cell08, 120 | Cell09 = battery.Cell09, 121 | Cell10 = battery.Cell10, 122 | Cell11 = battery.Cell11, 123 | Cell12 = battery.Cell12, 124 | Cell13 = battery.Cell13, 125 | Cell14 = battery.Cell14, 126 | Cell15 = battery.Cell15, 127 | Cell16 = battery.Cell16, 128 | MinPos = battery.MinPos, 129 | Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds, 130 | SlaveNumber = index 131 | }) 132 | .ToList(); 133 | 134 | if (batteryReads.Any(br => 135 | br.Cell01 == 0 || 136 | br.Cell05 == 0 || 137 | br.Cell09 == 0 || 138 | br.Cell13 == 0 139 | )) return; 140 | 141 | //SQLite 142 | using var scope = Services.CreateScope(); 143 | var scopedProcessingService = 144 | scope.ServiceProvider 145 | .GetRequiredService>(); 146 | 147 | foreach (BatteryCellReading reading in batteryReads) 148 | { 149 | await scopedProcessingService.AddAsync(reading); 150 | } 151 | } 152 | } 153 | catch (Exception exception) 154 | { 155 | _logger.LogError("{Exception}", exception.Message); 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /CAN2JSON/BackgroundServices/InfluxDbBackgroundService.cs: -------------------------------------------------------------------------------- 1 | using CAN2JSON.BMS; 2 | using CAN2JSON.Data.Measurements; 3 | using CAN2JSON.Data.Models; 4 | using InfluxDB.Client; 5 | using InfluxDB.Client.Api.Domain; 6 | using Task = System.Threading.Tasks.Task; 7 | 8 | namespace CAN2JSON.BackgroundServices; 9 | 10 | public class InfluxDbBackgroundService : BackgroundService 11 | { 12 | private readonly ApplicationInstance _application; 13 | private readonly ILogger _logger; 14 | private readonly IConfiguration _configuration; 15 | 16 | public InfluxDbBackgroundService(ILogger logger, 17 | ApplicationInstance application, IConfiguration configuration) 18 | { 19 | _logger = logger; 20 | _application = application; 21 | _configuration = configuration; 22 | } 23 | 24 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 25 | { 26 | _application.Application["rs485"] = new List(); 27 | var bucket = _configuration["InfluxDb:Bucket"]; 28 | var cellBucket = _configuration["InfluxDb:CellVoltageBucket"]; 29 | var org = _configuration["InfluxDb:Org"]; 30 | var url = _configuration["InfluxDb:Url"]; 31 | var interval = int.Parse(_configuration["BatterySerial:Interval"] ?? "2000"); 32 | if (bucket is null || org is null || url is null || cellBucket is null) throw new ArgumentNullException(); 33 | var client = 34 | InfluxDBClientFactory.Create(url, _configuration["InfluxDb:Token"]?.ToCharArray()); 35 | while (!stoppingToken.IsCancellationRequested) 36 | { 37 | await PerformFunction(bucket, cellBucket, client, org, stoppingToken); 38 | 39 | await Task.Delay(interval, stoppingToken); 40 | } 41 | } 42 | 43 | private Task PerformFunction(string bucket, string cellBucket, InfluxDBClient client, string org, CancellationToken stoppingToken) 44 | { 45 | if (_application.Application["bms"] is BatteryManagementSystem bms) 46 | { 47 | var tbms = bms; 48 | var bmsInflux = new BmsMeasurement() 49 | { 50 | Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds, 51 | ChargeCurrentLimit = tbms.ChargeCurrentLimit, 52 | ChargeCurrentLimitMax = tbms.ChargeCurrentLimitMax, 53 | Voltage = tbms.Voltage, 54 | Amps = tbms.Amps, 55 | Watts = tbms.Watts, 56 | Temperature = tbms.Temperature, 57 | StateOfCharge = tbms.StateOfCharge, 58 | StateOfHealth = tbms.StateOfHealth, 59 | CellVoltageHigh = tbms.CellVoltageHigh, 60 | CellVoltageLow = tbms.CellVoltageLow, 61 | CellVoltageDelta = tbms.CellVoltageDelta, 62 | BmsTemperatureHigh = tbms.BmsTemperatureHigh, 63 | BmsTemperatureLow = tbms.BmsTemperatureLow, 64 | BatteryCapacity = tbms.BatteryCapacity, 65 | ChargeVoltage = tbms.ChargeVoltage, 66 | CurrentLimit = tbms.CurrentLimit, 67 | DischargeLimit = tbms.DischargeLimit, 68 | BatteryCutoffVoltage = tbms.BatteryCutoffVoltage, 69 | FullChargedRestingVoltage = tbms.FullChargedRestingVoltage 70 | }; 71 | var battReadingsInflux = 72 | tbms.Batteries.Select((battery, index) => new BatteryMeasurement() 73 | { 74 | BatteryVoltage = battery.BatteryVoltage, 75 | BatteryCurrent = battery.BatteryCurrent, 76 | StateOfCharge = battery.StateOfCharge, 77 | StateOfHealth = battery.StateOfHealth, 78 | CellVoltageHigh = battery.CellVoltageHigh, 79 | CellVoltageLow = battery.CellVoltageLow, 80 | CellVoltageDelta = battery.CellVoltageDelta, 81 | TemperatureOne = battery.TemperatureOne, 82 | TemperatureTwo = battery.TemperatureTwo, 83 | TemperatureMos = battery.TemperatureMos, 84 | CurrentLimit = battery.CurrentLimit, 85 | CurrentLimitMax = battery.CurrentLimitMax, 86 | ChargedTotal = battery.ChargedTotal, 87 | DischargedTotal = battery.DischargedTotal, 88 | Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds, 89 | SlaveNumber = index 90 | }) 91 | .ToList(); 92 | 93 | if (battReadingsInflux.Any(br => 94 | br.DischargedTotal == 0 || 95 | br.ChargedTotal == 0 || 96 | br.TemperatureOne == 0 || 97 | br.TemperatureTwo == 0 || 98 | br.TemperatureMos == 0 99 | )) return Task.CompletedTask; 100 | 101 | if (tbms.Voltage == 0) return Task.CompletedTask; 102 | if (battReadingsInflux.Count != 2) return Task.CompletedTask; 103 | 104 | // Influx DB 105 | using var writeApi = client.GetWriteApi(); 106 | writeApi.WriteMeasurement(bucket, _configuration["InfluxDb:Org"], 107 | WritePrecision.Ns, bmsInflux); 108 | // Add battery measurements 109 | for (var index = 0; index < battReadingsInflux.Count; index++) 110 | { 111 | var batteryReading = battReadingsInflux[index]; 112 | writeApi.WriteMeasurement(_configuration["InfluxDb:Bucket"], _configuration["InfluxDb:Org"], 113 | WritePrecision.Ns, batteryReading); 114 | } 115 | } 116 | 117 | if (_application.Application["rs485"] is List bcm) 118 | { 119 | var bcms = bcm; 120 | var batteryCellMeasurements = bcms.Select((battery, index) => new BatteryCellMeasurement() 121 | { 122 | Cell01 = battery.Cell01, 123 | Cell02 = battery.Cell02, 124 | Cell03 = battery.Cell03, 125 | Cell04 = battery.Cell04, 126 | Cell05 = battery.Cell05, 127 | Cell06 = battery.Cell06, 128 | Cell07 = battery.Cell07, 129 | Cell08 = battery.Cell08, 130 | Cell09 = battery.Cell09, 131 | Cell10 = battery.Cell10, 132 | Cell11 = battery.Cell11, 133 | Cell12 = battery.Cell12, 134 | Cell13 = battery.Cell13, 135 | Cell14 = battery.Cell14, 136 | Cell15 = battery.Cell15, 137 | Cell16 = battery.Cell16, 138 | MinPos = battery.MinPos, 139 | Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds, 140 | SlaveNumber = index 141 | }) 142 | .ToList(); 143 | 144 | if (batteryCellMeasurements.Any(br => 145 | br.Cell01 == 0 || 146 | br.Cell05 == 0 || 147 | br.Cell09 == 0 || 148 | br.Cell13 == 0 149 | )) return Task.CompletedTask; 150 | 151 | // Add influx measurement 152 | using var writeApi = client.GetWriteApi(); 153 | foreach (var batteryCellMeasurment in batteryCellMeasurements) 154 | { 155 | writeApi.WriteMeasurement(cellBucket, org, 156 | WritePrecision.Ns, batteryCellMeasurment); 157 | } 158 | } 159 | 160 | return Task.CompletedTask; 161 | } 162 | } -------------------------------------------------------------------------------- /CAN2JSON/BackgroundServices/RS485BackgroundService.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Ports; 2 | using System.Text.Json; 3 | using System.Text.Json.Nodes; 4 | using CAN2JSON.Data.Measurements; 5 | using Task = System.Threading.Tasks.Task; 6 | 7 | namespace CAN2JSON.BackgroundServices; 8 | 9 | public class Rs485BackgroundService : BackgroundService 10 | { 11 | private readonly ILogger _logger; 12 | private readonly SerialPort _batteryOneSerial; 13 | private readonly SerialPort _batteryTwoSerial; 14 | private readonly JsonObject _document; 15 | private readonly ApplicationInstance _application; 16 | private readonly IConfiguration _configuration; 17 | private readonly byte[] _sendData = { 0xAA, 0x55, 0x01, 0x04, 0x00, 0x03, 0x70, 0x0D, 0x0A }; 18 | 19 | public Rs485BackgroundService(ILogger logger, ApplicationInstance application, 20 | IConfiguration configuration) 21 | { 22 | _logger = logger; 23 | _application = application; 24 | _configuration = configuration; 25 | _document = new JsonObject(); 26 | _batteryOneSerial = new SerialPort(); 27 | _batteryTwoSerial = new SerialPort(); 28 | } 29 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 30 | { 31 | _application.Application["jsonSerial"] = _document; 32 | var batteryReadings = new List(2); 33 | batteryReadings.Add(new BatteryCellMeasurement()); 34 | batteryReadings.Add(new BatteryCellMeasurement()); 35 | var batt1Port = _configuration["BatterySerial:Battery1"]; 36 | var batt2Port = _configuration["BatterySerial:Battery2"]; 37 | var interval = int.Parse(_configuration["BatterySerial:Interval"] ?? "5000"); 38 | if (batt1Port is null || batt2Port is null) 39 | throw new InvalidOperationException(); 40 | 41 | _batteryOneSerial.BaudRate = 9600; 42 | _batteryTwoSerial.BaudRate = 9600; 43 | _batteryOneSerial.PortName = batt1Port; 44 | _batteryTwoSerial.PortName = batt2Port; 45 | _batteryOneSerial.ReadTimeout = 100; 46 | _batteryTwoSerial.ReadTimeout = 100; 47 | 48 | _batteryOneSerial.Open(); 49 | _batteryTwoSerial.Open(); 50 | 51 | while (!stoppingToken.IsCancellationRequested) 52 | { 53 | await SerialWriteReadMeasurement_1(stoppingToken, batteryReadings); 54 | await SerialWriteReadMeasurement_2(stoppingToken, batteryReadings); 55 | 56 | await Task.Delay(interval, stoppingToken); 57 | } 58 | 59 | _batteryOneSerial.Close(); 60 | _batteryTwoSerial.Close(); 61 | } 62 | 63 | private async Task SerialWriteReadMeasurement_1(CancellationToken cancellationToken, 64 | List batteryReadings) 65 | { 66 | // Send data to the serial device 67 | _batteryOneSerial.Write(_sendData, 0, _sendData.Length); 68 | 69 | // Wait for a short time to ensure the data is sent 70 | // await Task.Delay(150, cancellationToken); 71 | 72 | var received = await ReadSerialResponse(_batteryOneSerial); 73 | batteryReadings[0] = ProcessResponse(received, 0); 74 | 75 | // Controller output 76 | _application.Application["jsonSerial"] = ToJsonSerial(batteryReadings); 77 | // BatteryCellReading list, for db workers to map and store 78 | _application.Application["rs485"] = batteryReadings; 79 | } 80 | 81 | private async Task SerialWriteReadMeasurement_2(CancellationToken cancellationToken, 82 | List batteryReadings) 83 | { 84 | // Send data to the serial device 85 | _batteryTwoSerial.Write(_sendData, 0, _sendData.Length); 86 | 87 | // Wait for a short time to ensure the data is sent 88 | // await Task.Delay(150, cancellationToken); 89 | 90 | var received = await ReadSerialResponse(_batteryTwoSerial); 91 | batteryReadings[1] = ProcessResponse(received, 1); 92 | 93 | // Controller output 94 | _application.Application["jsonSerial"] = ToJsonSerial(batteryReadings); 95 | // BatteryCellReading list, for db workers to map and store 96 | _application.Application["rs485"] = batteryReadings; 97 | } 98 | 99 | private async Task ReadSerialResponse(SerialPort serialPort) 100 | { 101 | // Read the response from the serial device 102 | byte[] buffer = new byte[28 * 3]; 103 | int bytesRead = await serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length); 104 | byte[] received = new byte[bytesRead]; 105 | Buffer.BlockCopy(buffer, 0, received, 0, bytesRead); 106 | 107 | _logger.LogInformation("{Date}: {SerialDevice}: {BytesRead} bytes: {Replace}", 108 | DateTime.UtcNow.ToLocalTime(), serialPort.PortName, bytesRead, 109 | BitConverter.ToString(received).Replace("-", " ")); 110 | return received; 111 | } 112 | 113 | public JsonObject ToJsonSerial(List batteryCellMeasurments) 114 | { 115 | var json = new JsonObject(); 116 | var serialArray = new JsonArray(); 117 | foreach (var batteryCell in batteryCellMeasurments) 118 | { 119 | using var document = JsonDocument.Parse(batteryCell.ToJson().ToString() ?? string.Empty); 120 | var frameJsonObject = document.RootElement.Clone(); 121 | serialArray.Add(frameJsonObject); 122 | } 123 | 124 | json["BatteryCells"] = serialArray; 125 | return json; 126 | } 127 | 128 | public BatteryCellMeasurement ProcessResponse(byte[] bytes, int slaveNumber) 129 | { 130 | var dataOffset = 13; 131 | var cellMeasurment = new BatteryCellMeasurement(); 132 | if (bytes.Length == 81) 133 | { 134 | cellMeasurment.MinPos = BitConverter.ToInt16(new[] { bytes[dataOffset - 3], bytes[dataOffset - 2] }); 135 | cellMeasurment.Cell01 = BitConverter.ToInt16(new[] { bytes[dataOffset + 1], bytes[dataOffset] }); 136 | cellMeasurment.Cell02 = BitConverter.ToInt16(new[] { bytes[dataOffset + 3], bytes[dataOffset + 2] }); 137 | cellMeasurment.Cell03 = BitConverter.ToInt16(new[] { bytes[dataOffset + 5], bytes[dataOffset + 4] }); 138 | cellMeasurment.Cell04 = BitConverter.ToInt16(new[] { bytes[dataOffset + 7], bytes[dataOffset + 6] }); 139 | cellMeasurment.Cell05 = BitConverter.ToInt16(new[] { bytes[dataOffset + 9], bytes[dataOffset + 8] }); 140 | cellMeasurment.Cell06 = BitConverter.ToInt16(new[] { bytes[dataOffset + 11], bytes[dataOffset + 10] }); 141 | cellMeasurment.Cell07 = BitConverter.ToInt16(new[] { bytes[dataOffset + 13], bytes[dataOffset + 12] }); 142 | cellMeasurment.Cell08 = BitConverter.ToInt16(new[] { bytes[dataOffset + 15], bytes[dataOffset + 14] }); 143 | cellMeasurment.Cell09 = BitConverter.ToInt16(new[] { bytes[dataOffset + 17], bytes[dataOffset + 16] }); 144 | cellMeasurment.Cell10 = BitConverter.ToInt16(new[] { bytes[dataOffset + 19], bytes[dataOffset + 18] }); 145 | cellMeasurment.Cell11 = BitConverter.ToInt16(new[] { bytes[dataOffset + 21], bytes[dataOffset + 20] }); 146 | cellMeasurment.Cell12 = BitConverter.ToInt16(new[] { bytes[dataOffset + 23], bytes[dataOffset + 22] }); 147 | cellMeasurment.Cell13 = BitConverter.ToInt16(new[] { bytes[dataOffset + 25], bytes[dataOffset + 24] }); 148 | cellMeasurment.Cell14 = BitConverter.ToInt16(new[] { bytes[dataOffset + 27], bytes[dataOffset + 26] }); 149 | cellMeasurment.Cell15 = BitConverter.ToInt16(new[] { bytes[dataOffset + 29], bytes[dataOffset + 28] }); 150 | cellMeasurment.Cell16 = BitConverter.ToInt16(new[] { bytes[dataOffset + 31], bytes[dataOffset + 30] }); 151 | cellMeasurment.Date = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; 152 | cellMeasurment.SlaveNumber = slaveNumber; 153 | } 154 | 155 | return cellMeasurment; 156 | } 157 | } -------------------------------------------------------------------------------- /RS485/RS485 data.log: -------------------------------------------------------------------------------- 1 | [2023-07-12 17:50:20.112] Send: AA 55 01 03 00 01 40 0D 0A 2 | [2023-07-12 17:50:20.112] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 3 | [2023-07-12 17:50:20.190] Send: AA 55 01 04 00 03 70 0D 0A 4 | [2023-07-12 17:50:20.190] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 5 | [2023-07-12 17:50:20.268] Send: AA 55 01 05 00 02 E0 0D 0A 6 | [2023-07-12 17:50:20.268] Recv: AA 55 01 04 48 0D 34 0F 0D 30 04 00 04 0D 31 0D 31 0D 31 0D 30 0D 31 0D 31 0D 31 0D 31 0D 33 0D 32 0D 32 0D 33 0D 33 0D 33 0D 34 0D 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4F 63 0D 0A 7 | [2023-07-12 17:50:20.331] Send: AA 55 01 01 00 00 20 0D 0A 8 | [2023-07-12 17:50:20.331] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 9 | [2023-07-12 17:50:20.409] Send: AA 55 01 02 00 00 D0 0D 0A 10 | [2023-07-12 17:50:20.409] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FD 03 AB 03 03 F2 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 89 4E 0D 0A 11 | [2023-07-12 17:50:20.475] Send: AA 55 01 03 00 01 40 0D 0A 12 | [2023-07-12 17:50:20.476] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 13 | [2023-07-12 17:50:20.534] Send: AA 55 01 04 00 03 70 0D 0A 14 | [2023-07-12 17:50:20.534] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 15 | [2023-07-12 17:50:20.612] Send: AA 55 01 05 00 02 E0 0D 0A 16 | [2023-07-12 17:50:20.612] Recv: AA 55 01 04 48 0D 34 0F 0D 30 04 00 04 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 33 0D 33 0D 32 0D 32 0D 33 0D 33 0D 34 0D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8B 0D 0D 0A 17 | [2023-07-12 17:50:20.674] Send: AA 55 01 01 00 00 20 0D 0A 18 | [2023-07-12 17:50:20.674] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 19 | [2023-07-12 17:50:20.752] Send: AA 55 01 02 00 00 D0 0D 0A 20 | [2023-07-12 17:50:20.752] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FC 03 AB 03 03 F3 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 80 0F 0D 0A 21 | [2023-07-12 17:50:20.831] Send: AA 55 01 03 00 01 40 0D 0A 22 | [2023-07-12 17:50:20.862] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 23 | [2023-07-12 17:50:20.909] Send: AA 55 01 04 00 03 70 0D 0A 24 | [2023-07-12 17:50:20.909] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 25 | [2023-07-12 17:50:20.982] Send: AA 55 01 05 00 02 E0 0D 0A 26 | [2023-07-12 17:50:20.982] Recv: AA 55 01 04 48 0D 34 0F 0D 31 01 00 03 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 33 0D 33 0D 32 0D 32 0D 33 0D 33 0D 34 0D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 3E 0D 0A 27 | [2023-07-12 17:50:21.034] Send: AA 55 01 01 00 00 20 0D 0A 28 | [2023-07-12 17:50:21.034] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 29 | [2023-07-12 17:50:21.112] Send: AA 55 01 02 00 00 D0 0D 0A 30 | [2023-07-12 17:50:21.112] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FD 03 AB 03 03 F4 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 89 A0 0D 0A 31 | [2023-07-12 17:50:21.190] Send: AA 55 01 03 00 01 40 0D 0A 32 | [2023-07-12 17:50:21.190] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 33 | [2023-07-12 17:50:21.268] Send: AA 55 01 04 00 03 70 0D 0A 34 | [2023-07-12 17:50:21.268] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 35 | [2023-07-12 17:50:21.347] Send: AA 55 01 05 00 02 E0 0D 0A 36 | [2023-07-12 17:50:21.347] Recv: AA 55 01 04 48 0D 34 0F 0D 30 03 00 04 0D 31 0D 31 0D 30 0D 31 0D 31 0D 31 0D 31 0D 31 0D 32 0D 33 0D 32 0D 32 0D 33 0D 33 0D 34 0D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DB D4 0D 0A 37 | [2023-07-12 17:50:21.409] Send: AA 55 01 01 00 00 20 0D 0A 38 | [2023-07-12 17:50:21.409] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 39 | [2023-07-12 17:50:21.487] Send: AA 55 01 02 00 00 D0 0D 0A 40 | [2023-07-12 17:50:21.487] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FD 03 AB 03 03 F5 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 88 4D 0D 0A 41 | [2023-07-12 17:50:21.565] Send: AA 55 01 03 00 01 40 0D 0A 42 | [2023-07-12 17:50:21.565] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 43 | [2023-07-12 17:50:21.643] Send: AA 55 01 04 00 03 70 0D 0A 44 | [2023-07-12 17:50:21.643] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 45 | [2023-07-12 17:50:21.721] Send: AA 55 01 05 00 02 E0 0D 0A 46 | [2023-07-12 17:50:21.721] Recv: AA 55 01 04 48 0D 34 0F 0D 31 01 00 03 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 33 0D 33 0D 32 0D 32 0D 33 0D 33 0D 34 0D 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0D FE 0D 0A 47 | [2023-07-12 17:50:21.784] Send: AA 55 01 01 00 00 20 0D 0A 48 | [2023-07-12 17:50:21.784] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 49 | [2023-07-12 17:50:21.862] Send: AA 55 01 02 00 00 D0 0D 0A 50 | [2023-07-12 17:50:21.862] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FE 03 AB 03 03 F6 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 93 8E 0D 0A 51 | [2023-07-12 17:50:21.940] Send: AA 55 01 03 00 01 40 0D 0A 52 | [2023-07-12 17:50:21.945] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 53 | [2023-07-12 17:50:22.003] Send: AA 55 01 04 00 03 70 0D 0A 54 | [2023-07-12 17:50:22.003] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 55 | [2023-07-12 17:50:22.081] Send: AA 55 01 05 00 02 E0 0D 0A 56 | [2023-07-12 17:50:22.081] Recv: AA 55 01 04 48 0D 34 0F 0D 31 01 00 03 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 33 0D 33 0D 32 0D 32 0D 33 0D 33 0D 34 0D 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0D FE 0D 0A 57 | [2023-07-12 17:50:22.143] Send: AA 55 01 01 00 00 20 0D 0A 58 | [2023-07-12 17:50:22.143] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 59 | [2023-07-12 17:50:22.222] Send: AA 55 01 02 00 00 D0 0D 0A 60 | [2023-07-12 17:50:22.222] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FD 03 AB 03 03 F7 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 89 D7 0D 0A 61 | [2023-07-12 17:50:22.299] Send: AA 55 01 03 00 01 40 0D 0A 62 | [2023-07-12 17:50:22.299] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 63 | [2023-07-12 17:50:22.377] Send: AA 55 01 04 00 03 70 0D 0A 64 | [2023-07-12 17:50:22.377] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 65 | [2023-07-12 17:50:22.451] Send: AA 55 01 05 00 02 E0 0D 0A 66 | [2023-07-12 17:50:22.451] Recv: AA 55 01 04 48 0D 34 0F 0D 30 08 00 04 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 30 0D 33 0D 33 0D 32 0D 33 0D 33 0D 33 0D 34 0D 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3E CD 0D 0A 67 | [2023-07-12 17:50:22.519] Send: AA 55 01 01 00 00 20 0D 0A 68 | [2023-07-12 17:50:22.519] Recv: AA 55 01 05 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 95 0D 0A 69 | [2023-07-12 17:50:22.597] Send: AA 55 01 02 00 00 D0 0D 0A 70 | [2023-07-12 17:50:22.597] Recv: AA 55 01 01 3D 00 00 03 AB 03 E8 39 3C 00 00 05 00 01 90 03 E8 00 00 0F 02 1C 0B FD 03 AB 03 03 F8 06 10 4C 56 45 53 53 30 32 32 33 35 32 36 4E 30 33 00 4C 56 45 53 53 30 32 5F 56 31 20 20 20 20 01 8A 3C 0D 0A 71 | [2023-07-12 17:50:22.674] Send: AA 55 01 03 00 01 40 0D 0A 72 | [2023-07-12 17:50:22.674] Recv: AA 55 01 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 B9 0F 0D 0A 73 | [2023-07-12 17:50:22.752] Send: AA 55 01 04 00 03 70 0D 0A 74 | [2023-07-12 17:50:22.752] Recv: AA 55 01 03 15 38 06 38 01 00 38 38 38 38 38 38 00 00 00 00 00 00 00 00 00 00 6A 17 0D 0A 75 | [2023-07-12 17:50:22.957] Recv: AA 55 01 04 48 0D 34 0F 0D 30 08 00 04 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 31 0D 30 0D 33 0D 33 0D 32 0D 32 0D 33 0D 33 0D 34 0D 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 39 70 0D 0A -------------------------------------------------------------------------------- /CAN2JSON/Migrations/Can2JsonContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using CAN2JSON.Data.Context; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | #nullable disable 9 | 10 | namespace CAN2JSON.Migrations 11 | { 12 | [DbContext(typeof(Can2JsonContext))] 13 | partial class Can2JsonContextModelSnapshot : ModelSnapshot 14 | { 15 | protected override void BuildModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); 19 | 20 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryCellReading", b => 21 | { 22 | b.Property("Id") 23 | .ValueGeneratedOnAdd() 24 | .HasColumnType("INTEGER"); 25 | 26 | b.Property("Cell01") 27 | .HasColumnType("decimal(4, 3)"); 28 | 29 | b.Property("Cell02") 30 | .HasColumnType("decimal(4, 3)"); 31 | 32 | b.Property("Cell03") 33 | .HasColumnType("decimal(4, 3)"); 34 | 35 | b.Property("Cell04") 36 | .HasColumnType("decimal(4, 3)"); 37 | 38 | b.Property("Cell05") 39 | .HasColumnType("decimal(4, 3)"); 40 | 41 | b.Property("Cell06") 42 | .HasColumnType("decimal(4, 3)"); 43 | 44 | b.Property("Cell07") 45 | .HasColumnType("decimal(4, 3)"); 46 | 47 | b.Property("Cell08") 48 | .HasColumnType("decimal(4, 3)"); 49 | 50 | b.Property("Cell09") 51 | .HasColumnType("decimal(4, 3)"); 52 | 53 | b.Property("Cell10") 54 | .HasColumnType("decimal(4, 3)"); 55 | 56 | b.Property("Cell11") 57 | .HasColumnType("decimal(4, 3)"); 58 | 59 | b.Property("Cell12") 60 | .HasColumnType("decimal(4, 3)"); 61 | 62 | b.Property("Cell13") 63 | .HasColumnType("decimal(4, 3)"); 64 | 65 | b.Property("Cell14") 66 | .HasColumnType("decimal(4, 3)"); 67 | 68 | b.Property("Cell15") 69 | .HasColumnType("decimal(4, 3)"); 70 | 71 | b.Property("Cell16") 72 | .HasColumnType("decimal(4, 3)"); 73 | 74 | b.Property("Date") 75 | .HasColumnType("INTEGER"); 76 | 77 | b.Property("MinPos") 78 | .HasColumnType("decimal(0, 0)"); 79 | 80 | b.Property("SlaveNumber") 81 | .HasColumnType("INTEGER"); 82 | 83 | b.HasKey("Id"); 84 | 85 | b.ToTable("BatteryCellReadings"); 86 | }); 87 | 88 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 89 | { 90 | b.Property("Id") 91 | .ValueGeneratedOnAdd() 92 | .HasColumnType("INTEGER"); 93 | 94 | b.Property("BatteryCurrent") 95 | .HasColumnType("decimal(3, 1)"); 96 | 97 | b.Property("BatteryVoltage") 98 | .HasColumnType("decimal(3, 1)"); 99 | 100 | b.Property("BmsReadingId") 101 | .HasColumnType("INTEGER"); 102 | 103 | b.Property("CellVoltageDelta") 104 | .HasColumnType("decimal(4, 3)"); 105 | 106 | b.Property("CellVoltageHigh") 107 | .HasColumnType("decimal(4, 3)"); 108 | 109 | b.Property("CellVoltageLow") 110 | .HasColumnType("decimal(4, 3)"); 111 | 112 | b.Property("ChargedTotal") 113 | .HasColumnType("decimal(7, 3)"); 114 | 115 | b.Property("CurrentLimit") 116 | .HasColumnType("decimal(3, 1)"); 117 | 118 | b.Property("CurrentLimitMax") 119 | .HasColumnType("decimal(3, 1)"); 120 | 121 | b.Property("Cycles") 122 | .HasColumnType("decimal(4, 3)"); 123 | 124 | b.Property("Date") 125 | .HasColumnType("INTEGER"); 126 | 127 | b.Property("DischargedTotal") 128 | .HasColumnType("decimal(7, 3)"); 129 | 130 | b.Property("SlaveNumber") 131 | .HasColumnType("INTEGER"); 132 | 133 | b.Property("StateOfCharge") 134 | .HasColumnType("decimal(3, 1)"); 135 | 136 | b.Property("StateOfHealth") 137 | .HasColumnType("decimal(3, 1)"); 138 | 139 | b.Property("TemperatureMos") 140 | .HasColumnType("decimal(3, 1)"); 141 | 142 | b.Property("TemperatureOne") 143 | .HasColumnType("decimal(3, 1)"); 144 | 145 | b.Property("TemperatureTwo") 146 | .HasColumnType("decimal(3, 1)"); 147 | 148 | b.HasKey("Id"); 149 | 150 | b.HasIndex("BmsReadingId"); 151 | 152 | b.ToTable("BatteryReadings"); 153 | }); 154 | 155 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 156 | { 157 | b.Property("Id") 158 | .ValueGeneratedOnAdd() 159 | .HasColumnType("INTEGER"); 160 | 161 | b.Property("Amps") 162 | .HasColumnType("decimal(3, 1)"); 163 | 164 | b.Property("BatteryCapacity") 165 | .HasColumnType("decimal(3, 1)"); 166 | 167 | b.Property("BatteryCutoffVoltage") 168 | .HasColumnType("decimal(3, 1)"); 169 | 170 | b.Property("BmsTemperatureHigh") 171 | .HasColumnType("decimal(3, 1)"); 172 | 173 | b.Property("BmsTemperatureLow") 174 | .HasColumnType("decimal(3, 1)"); 175 | 176 | b.Property("CellVoltageDelta") 177 | .HasColumnType("decimal(4, 3)"); 178 | 179 | b.Property("CellVoltageHigh") 180 | .HasColumnType("decimal(4, 3)"); 181 | 182 | b.Property("CellVoltageLow") 183 | .HasColumnType("decimal(4, 3)"); 184 | 185 | b.Property("ChargeCurrentLimit") 186 | .HasColumnType("decimal(3, 0)"); 187 | 188 | b.Property("ChargeCurrentLimitMax") 189 | .HasColumnType("decimal(3, 0)"); 190 | 191 | b.Property("ChargeVoltage") 192 | .HasColumnType("decimal(3, 1)"); 193 | 194 | b.Property("CurrentLimit") 195 | .HasColumnType("decimal(3, 1)"); 196 | 197 | b.Property("Date") 198 | .HasColumnType("INTEGER"); 199 | 200 | b.Property("DischargeLimit") 201 | .HasColumnType("decimal(3, 1)"); 202 | 203 | b.Property("FullChargedRestingVoltage") 204 | .HasColumnType("decimal(3, 1)"); 205 | 206 | b.Property("StateOfCharge") 207 | .HasColumnType("decimal(3, 1)"); 208 | 209 | b.Property("StateOfHealth") 210 | .HasColumnType("decimal(3, 1)"); 211 | 212 | b.Property("Temperature") 213 | .HasColumnType("decimal(3, 1)"); 214 | 215 | b.Property("Voltage") 216 | .HasColumnType("decimal(3, 1)"); 217 | 218 | b.Property("Watts") 219 | .HasColumnType("decimal(5, 1)"); 220 | 221 | b.HasKey("Id"); 222 | 223 | b.ToTable("BmsReadings"); 224 | }); 225 | 226 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 227 | { 228 | b.HasOne("CAN2JSON.Data.Models.BmsReading", null) 229 | .WithMany("BatteryReadings") 230 | .HasForeignKey("BmsReadingId"); 231 | }); 232 | 233 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 234 | { 235 | b.Navigation("BatteryReadings"); 236 | }); 237 | #pragma warning restore 612, 618 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230806054840_Add_BatteryCellReading_Model.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using CAN2JSON.Data.Context; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace CAN2JSON.Migrations 12 | { 13 | [DbContext(typeof(Can2JsonContext))] 14 | [Migration("20230806054840_Add_BatteryCellReading_Model")] 15 | partial class Add_BatteryCellReading_Model 16 | { 17 | /// 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); 22 | 23 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryCellReading", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd() 27 | .HasColumnType("INTEGER"); 28 | 29 | b.Property("Cell01") 30 | .HasColumnType("decimal(4, 3)"); 31 | 32 | b.Property("Cell02") 33 | .HasColumnType("decimal(4, 3)"); 34 | 35 | b.Property("Cell03") 36 | .HasColumnType("decimal(4, 3)"); 37 | 38 | b.Property("Cell04") 39 | .HasColumnType("decimal(4, 3)"); 40 | 41 | b.Property("Cell05") 42 | .HasColumnType("decimal(4, 3)"); 43 | 44 | b.Property("Cell06") 45 | .HasColumnType("decimal(4, 3)"); 46 | 47 | b.Property("Cell07") 48 | .HasColumnType("decimal(4, 3)"); 49 | 50 | b.Property("Cell08") 51 | .HasColumnType("decimal(4, 3)"); 52 | 53 | b.Property("Cell09") 54 | .HasColumnType("decimal(4, 3)"); 55 | 56 | b.Property("Cell10") 57 | .HasColumnType("decimal(4, 3)"); 58 | 59 | b.Property("Cell11") 60 | .HasColumnType("decimal(4, 3)"); 61 | 62 | b.Property("Cell12") 63 | .HasColumnType("decimal(4, 3)"); 64 | 65 | b.Property("Cell13") 66 | .HasColumnType("decimal(4, 3)"); 67 | 68 | b.Property("Cell14") 69 | .HasColumnType("decimal(4, 3)"); 70 | 71 | b.Property("Cell15") 72 | .HasColumnType("decimal(4, 3)"); 73 | 74 | b.Property("Cell16") 75 | .HasColumnType("decimal(4, 3)"); 76 | 77 | b.Property("Date") 78 | .HasColumnType("INTEGER"); 79 | 80 | b.Property("MinPos") 81 | .HasColumnType("decimal(0, 0)"); 82 | 83 | b.Property("SlaveNumber") 84 | .HasColumnType("INTEGER"); 85 | 86 | b.HasKey("Id"); 87 | 88 | b.ToTable("BatteryCellReadings"); 89 | }); 90 | 91 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 92 | { 93 | b.Property("Id") 94 | .ValueGeneratedOnAdd() 95 | .HasColumnType("INTEGER"); 96 | 97 | b.Property("BatteryCurrent") 98 | .HasColumnType("decimal(3, 1)"); 99 | 100 | b.Property("BatteryVoltage") 101 | .HasColumnType("decimal(3, 1)"); 102 | 103 | b.Property("BmsReadingId") 104 | .HasColumnType("INTEGER"); 105 | 106 | b.Property("CellVoltageDelta") 107 | .HasColumnType("decimal(4, 3)"); 108 | 109 | b.Property("CellVoltageHigh") 110 | .HasColumnType("decimal(4, 3)"); 111 | 112 | b.Property("CellVoltageLow") 113 | .HasColumnType("decimal(4, 3)"); 114 | 115 | b.Property("ChargedTotal") 116 | .HasColumnType("decimal(7, 3)"); 117 | 118 | b.Property("CurrentLimit") 119 | .HasColumnType("decimal(3, 1)"); 120 | 121 | b.Property("CurrentLimitMax") 122 | .HasColumnType("decimal(3, 1)"); 123 | 124 | b.Property("Cycles") 125 | .HasColumnType("decimal(4, 3)"); 126 | 127 | b.Property("Date") 128 | .HasColumnType("INTEGER"); 129 | 130 | b.Property("DischargedTotal") 131 | .HasColumnType("decimal(7, 3)"); 132 | 133 | b.Property("SlaveNumber") 134 | .HasColumnType("INTEGER"); 135 | 136 | b.Property("StateOfCharge") 137 | .HasColumnType("decimal(3, 1)"); 138 | 139 | b.Property("StateOfHealth") 140 | .HasColumnType("decimal(3, 1)"); 141 | 142 | b.Property("TemperatureMos") 143 | .HasColumnType("decimal(3, 1)"); 144 | 145 | b.Property("TemperatureOne") 146 | .HasColumnType("decimal(3, 1)"); 147 | 148 | b.Property("TemperatureTwo") 149 | .HasColumnType("decimal(3, 1)"); 150 | 151 | b.HasKey("Id"); 152 | 153 | b.HasIndex("BmsReadingId"); 154 | 155 | b.ToTable("BatteryReadings"); 156 | }); 157 | 158 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 159 | { 160 | b.Property("Id") 161 | .ValueGeneratedOnAdd() 162 | .HasColumnType("INTEGER"); 163 | 164 | b.Property("Amps") 165 | .HasColumnType("decimal(3, 1)"); 166 | 167 | b.Property("BatteryCapacity") 168 | .HasColumnType("decimal(3, 1)"); 169 | 170 | b.Property("BatteryCutoffVoltage") 171 | .HasColumnType("decimal(3, 1)"); 172 | 173 | b.Property("BmsTemperatureHigh") 174 | .HasColumnType("decimal(3, 1)"); 175 | 176 | b.Property("BmsTemperatureLow") 177 | .HasColumnType("decimal(3, 1)"); 178 | 179 | b.Property("CellVoltageDelta") 180 | .HasColumnType("decimal(4, 3)"); 181 | 182 | b.Property("CellVoltageHigh") 183 | .HasColumnType("decimal(4, 3)"); 184 | 185 | b.Property("CellVoltageLow") 186 | .HasColumnType("decimal(4, 3)"); 187 | 188 | b.Property("ChargeCurrentLimit") 189 | .HasColumnType("decimal(3, 0)"); 190 | 191 | b.Property("ChargeCurrentLimitMax") 192 | .HasColumnType("decimal(3, 0)"); 193 | 194 | b.Property("ChargeVoltage") 195 | .HasColumnType("decimal(3, 1)"); 196 | 197 | b.Property("CurrentLimit") 198 | .HasColumnType("decimal(3, 1)"); 199 | 200 | b.Property("Date") 201 | .HasColumnType("INTEGER"); 202 | 203 | b.Property("DischargeLimit") 204 | .HasColumnType("decimal(3, 1)"); 205 | 206 | b.Property("FullChargedRestingVoltage") 207 | .HasColumnType("decimal(3, 1)"); 208 | 209 | b.Property("StateOfCharge") 210 | .HasColumnType("decimal(3, 1)"); 211 | 212 | b.Property("StateOfHealth") 213 | .HasColumnType("decimal(3, 1)"); 214 | 215 | b.Property("Temperature") 216 | .HasColumnType("decimal(3, 1)"); 217 | 218 | b.Property("Voltage") 219 | .HasColumnType("decimal(3, 1)"); 220 | 221 | b.Property("Watts") 222 | .HasColumnType("decimal(5, 1)"); 223 | 224 | b.HasKey("Id"); 225 | 226 | b.ToTable("BmsReadings"); 227 | }); 228 | 229 | modelBuilder.Entity("CAN2JSON.Data.Models.BatteryReading", b => 230 | { 231 | b.HasOne("CAN2JSON.Data.Models.BmsReading", null) 232 | .WithMany("BatteryReadings") 233 | .HasForeignKey("BmsReadingId"); 234 | }); 235 | 236 | modelBuilder.Entity("CAN2JSON.Data.Models.BmsReading", b => 237 | { 238 | b.Navigation("BatteryReadings"); 239 | }); 240 | #pragma warning restore 612, 618 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /CAN2JSON/BackgroundServices/SerialDataBackgroundService.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers.Binary; 2 | using System.Globalization; 3 | using System.IO.Ports; 4 | using System.Text.Json.Nodes; 5 | using CAN2JSON.BMS; 6 | using HidSharp; 7 | 8 | namespace CAN2JSON.BackgroundServices; 9 | 10 | public class SerialDataBackgroundService : BackgroundService 11 | { 12 | private readonly ILogger _logger; 13 | private readonly SerialPort _serialPort; 14 | private readonly JsonObject _document; 15 | private readonly ApplicationInstance _application; 16 | private readonly BatteryManagementSystem _batteryManagementSystem = new(1); 17 | private bool _firstFrame = true; 18 | private bool _batteryCountSet; 19 | private readonly IConfiguration _configuration; 20 | /// 21 | /// Indicates whether the current application is running on Linux. 22 | /// 23 | public static bool IsLinux() => 24 | #if TARGET_LINUX && !TARGET_ANDROID 25 | true; 26 | #else 27 | false; 28 | #endif 29 | 30 | public SerialDataBackgroundService(ILogger logger, ApplicationInstance application, IConfiguration configuration) 31 | { 32 | _logger = logger; 33 | _application = application; 34 | _configuration = configuration; 35 | _document = new JsonObject(); 36 | _serialPort = new SerialPort(DetermineSerialDeviceName(), 2000000); 37 | } 38 | 39 | public string DetermineSerialDeviceName() 40 | { 41 | var list = DeviceList.Local; 42 | var allDeviceList = list.GetAllDevices().ToArray(); 43 | string devicePath = _configuration["CANDevice:Path"] ?? "/dev/ttyUSB0"; 44 | foreach (Device? device in allDeviceList) 45 | { 46 | if (!IsLinux()) 47 | { 48 | if (device.GetFriendlyName().Equals($"USB-SERIAL CH340 ({device.GetFileSystemName()})")) 49 | { 50 | devicePath = device.GetFileSystemName(); 51 | break; 52 | } 53 | } 54 | Console.WriteLine($"File System: {device.GetFileSystemName()}\nFriendly: {device.GetFriendlyName()}\n"); 55 | } 56 | 57 | // if (!IsLinux()) if (devicePath.Equals("/dev/ttyUSB0")) throw new InvalidOperationException(message:"No compatible serial devices found"); 58 | return devicePath; 59 | } 60 | 61 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 62 | { 63 | try 64 | { 65 | _application.Application["json"] = _document; 66 | _application.Application["bms"] = _batteryManagementSystem; 67 | 68 | 69 | _serialPort.Open(); 70 | _serialPort.DataReceived += SerialPort_DataReceived; 71 | 72 | while (!stoppingToken.IsCancellationRequested) 73 | { 74 | // Process serial data continuously 75 | await Task.Delay(100, stoppingToken); // Adjust the delay as needed 76 | } 77 | } 78 | catch (Exception ex) 79 | { 80 | _logger.LogError(ex, "An error occurred while processing serial data: ${Ex}", ex); 81 | } 82 | finally 83 | { 84 | _serialPort.DataReceived -= SerialPort_DataReceived; 85 | _serialPort.Close(); 86 | _serialPort.Dispose(); 87 | } 88 | } 89 | 90 | private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 91 | { 92 | SerialPort sp = (SerialPort)sender; 93 | int bytes = sp.BytesToRead; 94 | byte[] buffer = new byte[bytes]; 95 | sp.Read(buffer, 0, bytes); 96 | if (bytes is not 20) return; 97 | List littleEndianBytes = new List(bytes); 98 | littleEndianBytes.AddRange(buffer.Select(BinaryPrimitives.ReverseEndianness)); 99 | try 100 | { 101 | ProcessReceivedData(littleEndianBytes.ToArray()); 102 | } 103 | catch (Exception ex) 104 | { 105 | _logger.LogError(ex, "An error occurred in SerialPort_DataReceived: ${Ex}", ex); 106 | } 107 | } 108 | 109 | private void ProcessReceivedData(byte[] receivedData) 110 | { 111 | // Parse the received data and update the corresponding frame values 112 | /* 113 | * ** ** ** ** ** ID ID ** ** DS DB DB DB DB DB DB DB DB ** ?? 114 | * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 115 | * ----------------------------------------------------------- 116 | * AA-55-01-01-01-56-03-00-00-08-96-14-8B-00-82-00-00-00-00-1B 117 | */ 118 | 119 | if (receivedData.Length is not 20) return; 120 | var id = $"{BitConverter.ToString(new[] { receivedData[6], receivedData[5] }).Replace("-", string.Empty)}"; 121 | try 122 | { 123 | var canFrame = new CANFrame(id); 124 | canFrame.UpdateValues(receivedData); 125 | UpdateCanFrames(canFrame); 126 | _batteryManagementSystem.LastUpdate = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture); 127 | 128 | _application.Application["json"] = _batteryManagementSystem.ToJson(); 129 | _application.Application["bms"] = _batteryManagementSystem; 130 | } 131 | catch (Exception ex) 132 | { 133 | var errFrameData = BitConverter.ToString(receivedData); 134 | _logger.LogError(ex, errFrameData); 135 | } 136 | } 137 | 138 | private void UpdateCanFrames(CANFrame canFrame) 139 | { 140 | if (_firstFrame) 141 | { 142 | _batteryManagementSystem.CanFrames.Add(canFrame); 143 | // _batteryManagementSystem.XmlTemplate = File.ReadAllText(@"CANInformation\Deye slim.xml"); 144 | _firstFrame = false; 145 | } 146 | 147 | StatusUpdateAndAddBatteries(canFrame); 148 | 149 | UpdateBatteryInformation(canFrame); 150 | 151 | UpdateBmsData(canFrame); 152 | 153 | if (!_batteryManagementSystem.CanFrames.Any(cf => cf.FrameId.Equals(canFrame.FrameId))) 154 | { 155 | _batteryManagementSystem.CanFrames.Add(canFrame); 156 | } 157 | 158 | var copyArr = _batteryManagementSystem.CanFrames; 159 | for (var index = 0; index < copyArr.Count; index++) 160 | { 161 | if (copyArr[index].FrameId == canFrame.FrameId) copyArr[index] = canFrame; 162 | } 163 | 164 | _batteryManagementSystem.CanFrames = copyArr; 165 | } 166 | 167 | private void UpdateBatteryInformation(CANFrame canFrame) 168 | { 169 | for (int i = 0; i < _batteryManagementSystem.Batteries.Count; i++) 170 | { 171 | if (canFrame.FrameId.Equals($"015{i}")) 172 | { 173 | _batteryManagementSystem.Batteries[i].BatteryVoltage = canFrame.DataInShorts[0] / 10m; 174 | _batteryManagementSystem.Batteries[i].BatteryCurrent = canFrame.DataInShorts[1] / -10m; 175 | _batteryManagementSystem.Batteries[i].StateOfCharge = canFrame.DataInShorts[2] / 10m; 176 | _batteryManagementSystem.Batteries[i].StateOfHealth = canFrame.DataInShorts[3] / 10m; 177 | continue; 178 | } 179 | 180 | if (canFrame.FrameId.Equals($"020{i}")) 181 | { 182 | _batteryManagementSystem.Batteries[i].CellVoltageHigh = canFrame.DataInShorts[0] / 1000m; 183 | _batteryManagementSystem.Batteries[i].CellVoltageLow = canFrame.DataInShorts[1] / 1000m; 184 | _batteryManagementSystem.Batteries[i].TemperatureOne = canFrame.DataInShorts[2] / 10m; 185 | _batteryManagementSystem.Batteries[i].TemperatureTwo = canFrame.DataInShorts[3] / 10m; 186 | _batteryManagementSystem.Batteries[i].CellVoltageDelta = 187 | canFrame.DataInShorts[0] / 1000m - canFrame.DataInShorts[1] / 1000m; 188 | 189 | continue; 190 | } 191 | 192 | if (canFrame.FrameId.Equals($"025{i}")) 193 | { 194 | _batteryManagementSystem.Batteries[i].TemperatureMos = canFrame.DataInShorts[0] / 10m; 195 | // _batteryManagementSystem.Batteries[i].CurrentLimit = canFrame.DataInShorts[1]/10m; 196 | _batteryManagementSystem.Batteries[i].CurrentLimit = canFrame.DataInShorts[2] / 1m; 197 | _batteryManagementSystem.Batteries[i].CurrentLimitMax = canFrame.DataInShorts[3] / 1m; 198 | continue; 199 | } 200 | 201 | if (canFrame.FrameId.Equals($"040{i}")) 202 | { 203 | _batteryManagementSystem.Batteries[i].Status = new BatteryBmsStatuses 204 | { 205 | Status1 = canFrame.Data[10], 206 | Status2 = canFrame.Data[11], 207 | Status3 = canFrame.Data[12], 208 | Status4 = canFrame.Data[13], 209 | Status5 = canFrame.Data[14], 210 | Status6 = canFrame.Data[15], 211 | Status7 = canFrame.Data[16], 212 | Status8 = canFrame.Data[17] 213 | }; 214 | continue; 215 | } 216 | 217 | if (canFrame.FrameId.Equals($"055{i}")) 218 | { 219 | _batteryManagementSystem.Batteries[i].ChargedTotal = canFrame.DataInInt32[0] / 1000m; 220 | _batteryManagementSystem.Batteries[i].DischargedTotal = canFrame.DataInInt32[1] / 1000m; 221 | _batteryManagementSystem.Batteries[i].Cycles = 222 | _batteryManagementSystem.Batteries[i].ChargedTotal * 1000 / 5120m; 223 | } 224 | } 225 | } 226 | 227 | private void UpdateBmsData(CANFrame canFrame) 228 | { 229 | for (int i = 0; i < _batteryManagementSystem.Batteries.Count; i++) 230 | { 231 | if (canFrame.FrameId.Equals("0351")) 232 | { 233 | _batteryManagementSystem.ChargeVoltage = canFrame.DataInShorts[0] / 10m; 234 | _batteryManagementSystem.ChargeCurrentLimit = canFrame.DataInShorts[1] / 10m; 235 | _batteryManagementSystem.ChargeCurrentLimitMax = canFrame.DataInShorts[2] / 10m; 236 | _batteryManagementSystem.BatteryCutoffVoltage = canFrame.DataInShorts[3] / 10m; 237 | continue; 238 | } 239 | 240 | if (canFrame.FrameId.Equals("0371")) 241 | { 242 | _batteryManagementSystem.CurrentLimit = canFrame.DataInShorts[0] / 10m; 243 | _batteryManagementSystem.DischargeLimit = canFrame.DataInShorts[1] / 10m; 244 | continue; 245 | } 246 | 247 | if (canFrame.FrameId.Equals("0355")) 248 | { 249 | _batteryManagementSystem.StateOfCharge = canFrame.DataInShorts[0] / 1m; 250 | _batteryManagementSystem.StateOfHealth = canFrame.DataInShorts[1] / 1m; 251 | continue; 252 | } 253 | 254 | if (canFrame.FrameId.Equals("0356")) 255 | { 256 | _batteryManagementSystem.Voltage = canFrame.DataInShorts[0] / 100m; 257 | _batteryManagementSystem.Amps = canFrame.DataInShorts[1] / -10m; 258 | _batteryManagementSystem.Temperature = canFrame.DataInShorts[2] / 10m; 259 | _batteryManagementSystem.Watts = _batteryManagementSystem.Voltage * _batteryManagementSystem.Amps / 1m; 260 | continue; 261 | } 262 | 263 | if (canFrame.FrameId.Equals("0361")) 264 | { 265 | _batteryManagementSystem.CellVoltageHigh = canFrame.DataInShorts[0] / 1000m; 266 | _batteryManagementSystem.CellVoltageLow = canFrame.DataInShorts[1] / 1000m; 267 | _batteryManagementSystem.BmsTemperatureHigh = canFrame.DataInShorts[2] / 10m; 268 | _batteryManagementSystem.BmsTemperatureLow = canFrame.DataInShorts[3] / 10m; 269 | _batteryManagementSystem.CellVoltageDelta = 270 | canFrame.DataInShorts[0] / 1000m - canFrame.DataInShorts[1] / 1000m; 271 | continue; 272 | } 273 | 274 | if (canFrame.FrameId.Equals("0363")) 275 | { 276 | _batteryManagementSystem.BatteryCapacity = canFrame.DataInShorts[0] / 10m; 277 | _batteryManagementSystem.FullChargedRestingVoltage = canFrame.DataInShorts[1] / 10m; 278 | } 279 | } 280 | } 281 | 282 | private void StatusUpdateAndAddBatteries(CANFrame canFrame) 283 | { 284 | if (!canFrame.FrameId.Equals("0364")) return; 285 | _batteryManagementSystem.Statuses = new BmsStatuses 286 | { 287 | Status1 = canFrame.Data[10], 288 | Status2 = canFrame.Data[11], 289 | Status3 = canFrame.Data[12], 290 | Status4 = canFrame.Data[13], 291 | Status5 = canFrame.Data[14], 292 | Status6 = canFrame.Data[15], 293 | Status7 = canFrame.Data[16], 294 | Status8 = canFrame.Data[17] 295 | }; 296 | if (_batteryCountSet) return; 297 | _batteryCountSet = true; 298 | var batCount = _batteryManagementSystem.Statuses.Status5; 299 | _batteryManagementSystem.Batteries = new List(batCount); 300 | var batteries = new List(batCount); 301 | for (var i = 0; i < batCount; i++) 302 | { 303 | var battery = new Battery(); 304 | batteries.Add(battery); 305 | } 306 | 307 | _batteryManagementSystem.Batteries.AddRange(batteries); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /CAN2JSON/Migrations/20230714213902_DataTypes.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace CAN2JSON.Migrations 6 | { 7 | /// 8 | public partial class DataTypes : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AlterColumn( 14 | name: "Watts", 15 | table: "BmsReadings", 16 | type: "decimal(5, 1)", 17 | nullable: false, 18 | oldClrType: typeof(decimal), 19 | oldType: "TEXT"); 20 | 21 | migrationBuilder.AlterColumn( 22 | name: "Voltage", 23 | table: "BmsReadings", 24 | type: "decimal(3, 1)", 25 | nullable: false, 26 | oldClrType: typeof(decimal), 27 | oldType: "TEXT"); 28 | 29 | migrationBuilder.AlterColumn( 30 | name: "Temperature", 31 | table: "BmsReadings", 32 | type: "decimal(3, 1)", 33 | nullable: false, 34 | oldClrType: typeof(decimal), 35 | oldType: "TEXT"); 36 | 37 | migrationBuilder.AlterColumn( 38 | name: "StateOfHealth", 39 | table: "BmsReadings", 40 | type: "decimal(3, 1)", 41 | nullable: false, 42 | oldClrType: typeof(decimal), 43 | oldType: "TEXT"); 44 | 45 | migrationBuilder.AlterColumn( 46 | name: "StateOfCharge", 47 | table: "BmsReadings", 48 | type: "decimal(3, 1)", 49 | nullable: false, 50 | oldClrType: typeof(decimal), 51 | oldType: "TEXT"); 52 | 53 | migrationBuilder.AlterColumn( 54 | name: "FullChargedRestingVoltage", 55 | table: "BmsReadings", 56 | type: "decimal(3, 1)", 57 | nullable: false, 58 | oldClrType: typeof(decimal), 59 | oldType: "TEXT"); 60 | 61 | migrationBuilder.AlterColumn( 62 | name: "DischargeLimit", 63 | table: "BmsReadings", 64 | type: "decimal(3, 1)", 65 | nullable: false, 66 | oldClrType: typeof(decimal), 67 | oldType: "TEXT"); 68 | 69 | migrationBuilder.AlterColumn( 70 | name: "CurrentLimit", 71 | table: "BmsReadings", 72 | type: "decimal(3, 1)", 73 | nullable: false, 74 | oldClrType: typeof(decimal), 75 | oldType: "TEXT"); 76 | 77 | migrationBuilder.AlterColumn( 78 | name: "ChargeVoltage", 79 | table: "BmsReadings", 80 | type: "decimal(3, 1)", 81 | nullable: false, 82 | oldClrType: typeof(decimal), 83 | oldType: "TEXT"); 84 | 85 | migrationBuilder.AlterColumn( 86 | name: "ChargeCurrentLimitMax", 87 | table: "BmsReadings", 88 | type: "decimal(3, 0)", 89 | nullable: false, 90 | oldClrType: typeof(decimal), 91 | oldType: "TEXT"); 92 | 93 | migrationBuilder.AlterColumn( 94 | name: "ChargeCurrentLimit", 95 | table: "BmsReadings", 96 | type: "decimal(3, 0)", 97 | nullable: false, 98 | oldClrType: typeof(decimal), 99 | oldType: "TEXT"); 100 | 101 | migrationBuilder.AlterColumn( 102 | name: "CellVoltageLow", 103 | table: "BmsReadings", 104 | type: "decimal(4, 3)", 105 | nullable: false, 106 | oldClrType: typeof(decimal), 107 | oldType: "TEXT"); 108 | 109 | migrationBuilder.AlterColumn( 110 | name: "CellVoltageHigh", 111 | table: "BmsReadings", 112 | type: "decimal(4, 3)", 113 | nullable: false, 114 | oldClrType: typeof(decimal), 115 | oldType: "TEXT"); 116 | 117 | migrationBuilder.AlterColumn( 118 | name: "CellVoltageDelta", 119 | table: "BmsReadings", 120 | type: "decimal(4, 3)", 121 | nullable: false, 122 | oldClrType: typeof(decimal), 123 | oldType: "TEXT"); 124 | 125 | migrationBuilder.AlterColumn( 126 | name: "BmsTemperatureLow", 127 | table: "BmsReadings", 128 | type: "decimal(3, 1)", 129 | nullable: false, 130 | oldClrType: typeof(decimal), 131 | oldType: "TEXT"); 132 | 133 | migrationBuilder.AlterColumn( 134 | name: "BmsTemperatureHigh", 135 | table: "BmsReadings", 136 | type: "decimal(3, 1)", 137 | nullable: false, 138 | oldClrType: typeof(decimal), 139 | oldType: "TEXT"); 140 | 141 | migrationBuilder.AlterColumn( 142 | name: "BatteryCutoffVoltage", 143 | table: "BmsReadings", 144 | type: "decimal(3, 1)", 145 | nullable: false, 146 | oldClrType: typeof(decimal), 147 | oldType: "TEXT"); 148 | 149 | migrationBuilder.AlterColumn( 150 | name: "BatteryCapacity", 151 | table: "BmsReadings", 152 | type: "decimal(3, 1)", 153 | nullable: false, 154 | oldClrType: typeof(decimal), 155 | oldType: "TEXT"); 156 | 157 | migrationBuilder.AlterColumn( 158 | name: "Amps", 159 | table: "BmsReadings", 160 | type: "decimal(3, 1)", 161 | nullable: false, 162 | oldClrType: typeof(decimal), 163 | oldType: "TEXT"); 164 | 165 | migrationBuilder.AlterColumn( 166 | name: "TemperatureTwo", 167 | table: "BatteryReadings", 168 | type: "decimal(3, 1)", 169 | nullable: false, 170 | oldClrType: typeof(decimal), 171 | oldType: "TEXT"); 172 | 173 | migrationBuilder.AlterColumn( 174 | name: "TemperatureOne", 175 | table: "BatteryReadings", 176 | type: "decimal(3, 1)", 177 | nullable: false, 178 | oldClrType: typeof(decimal), 179 | oldType: "TEXT"); 180 | 181 | migrationBuilder.AlterColumn( 182 | name: "TemperatureMos", 183 | table: "BatteryReadings", 184 | type: "decimal(3, 1)", 185 | nullable: false, 186 | oldClrType: typeof(decimal), 187 | oldType: "TEXT"); 188 | 189 | migrationBuilder.AlterColumn( 190 | name: "StateOfHealth", 191 | table: "BatteryReadings", 192 | type: "decimal(3, 1)", 193 | nullable: false, 194 | oldClrType: typeof(decimal), 195 | oldType: "TEXT"); 196 | 197 | migrationBuilder.AlterColumn( 198 | name: "StateOfCharge", 199 | table: "BatteryReadings", 200 | type: "decimal(3, 1)", 201 | nullable: false, 202 | oldClrType: typeof(decimal), 203 | oldType: "TEXT"); 204 | 205 | migrationBuilder.AlterColumn( 206 | name: "DischargedTotal", 207 | table: "BatteryReadings", 208 | type: "decimal(7, 3)", 209 | nullable: false, 210 | oldClrType: typeof(decimal), 211 | oldType: "TEXT"); 212 | 213 | migrationBuilder.AlterColumn( 214 | name: "Cycles", 215 | table: "BatteryReadings", 216 | type: "decimal(4, 3)", 217 | nullable: false, 218 | oldClrType: typeof(decimal), 219 | oldType: "TEXT"); 220 | 221 | migrationBuilder.AlterColumn( 222 | name: "CurrentLimitMax", 223 | table: "BatteryReadings", 224 | type: "decimal(3, 1)", 225 | nullable: false, 226 | oldClrType: typeof(decimal), 227 | oldType: "TEXT"); 228 | 229 | migrationBuilder.AlterColumn( 230 | name: "CurrentLimit", 231 | table: "BatteryReadings", 232 | type: "decimal(3, 1)", 233 | nullable: false, 234 | oldClrType: typeof(decimal), 235 | oldType: "TEXT"); 236 | 237 | migrationBuilder.AlterColumn( 238 | name: "ChargedTotal", 239 | table: "BatteryReadings", 240 | type: "decimal(7, 3)", 241 | nullable: false, 242 | oldClrType: typeof(decimal), 243 | oldType: "TEXT"); 244 | 245 | migrationBuilder.AlterColumn( 246 | name: "CellVoltageLow", 247 | table: "BatteryReadings", 248 | type: "decimal(4, 3)", 249 | nullable: false, 250 | oldClrType: typeof(decimal), 251 | oldType: "TEXT"); 252 | 253 | migrationBuilder.AlterColumn( 254 | name: "CellVoltageHigh", 255 | table: "BatteryReadings", 256 | type: "decimal(4, 3)", 257 | nullable: false, 258 | oldClrType: typeof(decimal), 259 | oldType: "TEXT"); 260 | 261 | migrationBuilder.AlterColumn( 262 | name: "CellVoltageDelta", 263 | table: "BatteryReadings", 264 | type: "decimal(4, 3)", 265 | nullable: false, 266 | oldClrType: typeof(decimal), 267 | oldType: "TEXT"); 268 | 269 | migrationBuilder.AlterColumn( 270 | name: "BatteryVoltage", 271 | table: "BatteryReadings", 272 | type: "decimal(3, 1)", 273 | nullable: false, 274 | oldClrType: typeof(decimal), 275 | oldType: "TEXT"); 276 | 277 | migrationBuilder.AlterColumn( 278 | name: "BatteryCurrent", 279 | table: "BatteryReadings", 280 | type: "decimal(3, 1)", 281 | nullable: false, 282 | oldClrType: typeof(decimal), 283 | oldType: "TEXT"); 284 | } 285 | 286 | /// 287 | protected override void Down(MigrationBuilder migrationBuilder) 288 | { 289 | migrationBuilder.AlterColumn( 290 | name: "Watts", 291 | table: "BmsReadings", 292 | type: "TEXT", 293 | nullable: false, 294 | oldClrType: typeof(decimal), 295 | oldType: "decimal(5, 1)"); 296 | 297 | migrationBuilder.AlterColumn( 298 | name: "Voltage", 299 | table: "BmsReadings", 300 | type: "TEXT", 301 | nullable: false, 302 | oldClrType: typeof(decimal), 303 | oldType: "decimal(3, 1)"); 304 | 305 | migrationBuilder.AlterColumn( 306 | name: "Temperature", 307 | table: "BmsReadings", 308 | type: "TEXT", 309 | nullable: false, 310 | oldClrType: typeof(decimal), 311 | oldType: "decimal(3, 1)"); 312 | 313 | migrationBuilder.AlterColumn( 314 | name: "StateOfHealth", 315 | table: "BmsReadings", 316 | type: "TEXT", 317 | nullable: false, 318 | oldClrType: typeof(decimal), 319 | oldType: "decimal(3, 1)"); 320 | 321 | migrationBuilder.AlterColumn( 322 | name: "StateOfCharge", 323 | table: "BmsReadings", 324 | type: "TEXT", 325 | nullable: false, 326 | oldClrType: typeof(decimal), 327 | oldType: "decimal(3, 1)"); 328 | 329 | migrationBuilder.AlterColumn( 330 | name: "FullChargedRestingVoltage", 331 | table: "BmsReadings", 332 | type: "TEXT", 333 | nullable: false, 334 | oldClrType: typeof(decimal), 335 | oldType: "decimal(3, 1)"); 336 | 337 | migrationBuilder.AlterColumn( 338 | name: "DischargeLimit", 339 | table: "BmsReadings", 340 | type: "TEXT", 341 | nullable: false, 342 | oldClrType: typeof(decimal), 343 | oldType: "decimal(3, 1)"); 344 | 345 | migrationBuilder.AlterColumn( 346 | name: "CurrentLimit", 347 | table: "BmsReadings", 348 | type: "TEXT", 349 | nullable: false, 350 | oldClrType: typeof(decimal), 351 | oldType: "decimal(3, 1)"); 352 | 353 | migrationBuilder.AlterColumn( 354 | name: "ChargeVoltage", 355 | table: "BmsReadings", 356 | type: "TEXT", 357 | nullable: false, 358 | oldClrType: typeof(decimal), 359 | oldType: "decimal(3, 1)"); 360 | 361 | migrationBuilder.AlterColumn( 362 | name: "ChargeCurrentLimitMax", 363 | table: "BmsReadings", 364 | type: "TEXT", 365 | nullable: false, 366 | oldClrType: typeof(decimal), 367 | oldType: "decimal(3, 0)"); 368 | 369 | migrationBuilder.AlterColumn( 370 | name: "ChargeCurrentLimit", 371 | table: "BmsReadings", 372 | type: "TEXT", 373 | nullable: false, 374 | oldClrType: typeof(decimal), 375 | oldType: "decimal(3, 0)"); 376 | 377 | migrationBuilder.AlterColumn( 378 | name: "CellVoltageLow", 379 | table: "BmsReadings", 380 | type: "TEXT", 381 | nullable: false, 382 | oldClrType: typeof(decimal), 383 | oldType: "decimal(4, 3)"); 384 | 385 | migrationBuilder.AlterColumn( 386 | name: "CellVoltageHigh", 387 | table: "BmsReadings", 388 | type: "TEXT", 389 | nullable: false, 390 | oldClrType: typeof(decimal), 391 | oldType: "decimal(4, 3)"); 392 | 393 | migrationBuilder.AlterColumn( 394 | name: "CellVoltageDelta", 395 | table: "BmsReadings", 396 | type: "TEXT", 397 | nullable: false, 398 | oldClrType: typeof(decimal), 399 | oldType: "decimal(4, 3)"); 400 | 401 | migrationBuilder.AlterColumn( 402 | name: "BmsTemperatureLow", 403 | table: "BmsReadings", 404 | type: "TEXT", 405 | nullable: false, 406 | oldClrType: typeof(decimal), 407 | oldType: "decimal(3, 1)"); 408 | 409 | migrationBuilder.AlterColumn( 410 | name: "BmsTemperatureHigh", 411 | table: "BmsReadings", 412 | type: "TEXT", 413 | nullable: false, 414 | oldClrType: typeof(decimal), 415 | oldType: "decimal(3, 1)"); 416 | 417 | migrationBuilder.AlterColumn( 418 | name: "BatteryCutoffVoltage", 419 | table: "BmsReadings", 420 | type: "TEXT", 421 | nullable: false, 422 | oldClrType: typeof(decimal), 423 | oldType: "decimal(3, 1)"); 424 | 425 | migrationBuilder.AlterColumn( 426 | name: "BatteryCapacity", 427 | table: "BmsReadings", 428 | type: "TEXT", 429 | nullable: false, 430 | oldClrType: typeof(decimal), 431 | oldType: "decimal(3, 1)"); 432 | 433 | migrationBuilder.AlterColumn( 434 | name: "Amps", 435 | table: "BmsReadings", 436 | type: "TEXT", 437 | nullable: false, 438 | oldClrType: typeof(decimal), 439 | oldType: "decimal(3, 1)"); 440 | 441 | migrationBuilder.AlterColumn( 442 | name: "TemperatureTwo", 443 | table: "BatteryReadings", 444 | type: "TEXT", 445 | nullable: false, 446 | oldClrType: typeof(decimal), 447 | oldType: "decimal(3, 1)"); 448 | 449 | migrationBuilder.AlterColumn( 450 | name: "TemperatureOne", 451 | table: "BatteryReadings", 452 | type: "TEXT", 453 | nullable: false, 454 | oldClrType: typeof(decimal), 455 | oldType: "decimal(3, 1)"); 456 | 457 | migrationBuilder.AlterColumn( 458 | name: "TemperatureMos", 459 | table: "BatteryReadings", 460 | type: "TEXT", 461 | nullable: false, 462 | oldClrType: typeof(decimal), 463 | oldType: "decimal(3, 1)"); 464 | 465 | migrationBuilder.AlterColumn( 466 | name: "StateOfHealth", 467 | table: "BatteryReadings", 468 | type: "TEXT", 469 | nullable: false, 470 | oldClrType: typeof(decimal), 471 | oldType: "decimal(3, 1)"); 472 | 473 | migrationBuilder.AlterColumn( 474 | name: "StateOfCharge", 475 | table: "BatteryReadings", 476 | type: "TEXT", 477 | nullable: false, 478 | oldClrType: typeof(decimal), 479 | oldType: "decimal(3, 1)"); 480 | 481 | migrationBuilder.AlterColumn( 482 | name: "DischargedTotal", 483 | table: "BatteryReadings", 484 | type: "TEXT", 485 | nullable: false, 486 | oldClrType: typeof(decimal), 487 | oldType: "decimal(7, 3)"); 488 | 489 | migrationBuilder.AlterColumn( 490 | name: "Cycles", 491 | table: "BatteryReadings", 492 | type: "TEXT", 493 | nullable: false, 494 | oldClrType: typeof(decimal), 495 | oldType: "decimal(4, 3)"); 496 | 497 | migrationBuilder.AlterColumn( 498 | name: "CurrentLimitMax", 499 | table: "BatteryReadings", 500 | type: "TEXT", 501 | nullable: false, 502 | oldClrType: typeof(decimal), 503 | oldType: "decimal(3, 1)"); 504 | 505 | migrationBuilder.AlterColumn( 506 | name: "CurrentLimit", 507 | table: "BatteryReadings", 508 | type: "TEXT", 509 | nullable: false, 510 | oldClrType: typeof(decimal), 511 | oldType: "decimal(3, 1)"); 512 | 513 | migrationBuilder.AlterColumn( 514 | name: "ChargedTotal", 515 | table: "BatteryReadings", 516 | type: "TEXT", 517 | nullable: false, 518 | oldClrType: typeof(decimal), 519 | oldType: "decimal(7, 3)"); 520 | 521 | migrationBuilder.AlterColumn( 522 | name: "CellVoltageLow", 523 | table: "BatteryReadings", 524 | type: "TEXT", 525 | nullable: false, 526 | oldClrType: typeof(decimal), 527 | oldType: "decimal(4, 3)"); 528 | 529 | migrationBuilder.AlterColumn( 530 | name: "CellVoltageHigh", 531 | table: "BatteryReadings", 532 | type: "TEXT", 533 | nullable: false, 534 | oldClrType: typeof(decimal), 535 | oldType: "decimal(4, 3)"); 536 | 537 | migrationBuilder.AlterColumn( 538 | name: "CellVoltageDelta", 539 | table: "BatteryReadings", 540 | type: "TEXT", 541 | nullable: false, 542 | oldClrType: typeof(decimal), 543 | oldType: "decimal(4, 3)"); 544 | 545 | migrationBuilder.AlterColumn( 546 | name: "BatteryVoltage", 547 | table: "BatteryReadings", 548 | type: "TEXT", 549 | nullable: false, 550 | oldClrType: typeof(decimal), 551 | oldType: "decimal(3, 1)"); 552 | 553 | migrationBuilder.AlterColumn( 554 | name: "BatteryCurrent", 555 | table: "BatteryReadings", 556 | type: "TEXT", 557 | nullable: false, 558 | oldClrType: typeof(decimal), 559 | oldType: "decimal(3, 1)"); 560 | } 561 | } 562 | } 563 | --------------------------------------------------------------------------------