completed the update PVT function
This commit is contained in:
commit
13f8a31251
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
RuleEngine/bin/
|
||||||
|
RuleEngine/obj/
|
||||||
|
RuleEngine/publish/
|
||||||
|
SimulatedTrackingDevice/obj/
|
16
RuleEngine/Common/LimitedSizeQueue.cs
Normal file
16
RuleEngine/Common/LimitedSizeQueue.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
namespace RuleEngine.Common
|
||||||
|
{
|
||||||
|
public class LimitedSizeQueue<T>(int limit) : Queue<T>(limit)
|
||||||
|
{
|
||||||
|
public int Limit { get; set; } = limit;
|
||||||
|
|
||||||
|
public new void Enqueue(T item)
|
||||||
|
{
|
||||||
|
while (Count >= Limit)
|
||||||
|
{
|
||||||
|
Dequeue();
|
||||||
|
}
|
||||||
|
base.Enqueue(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
224
RuleEngine/Common/StaticResources.cs
Normal file
224
RuleEngine/Common/StaticResources.cs
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace RuleEngine.Common
|
||||||
|
{
|
||||||
|
public class StaticResources
|
||||||
|
{
|
||||||
|
public static void Log2File(string filePath, string data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo info = new FileInfo(filePath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (info.Exists && info.Length > 10000000) // delete the file first if 10 MB
|
||||||
|
{
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
using var fs = File.Open(filePath, FileMode.Append);
|
||||||
|
using var sw = new StreamWriter(fs);
|
||||||
|
sw.WriteLine(data);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void Log2FileByMonth(string folderPath,string fileName, string data,bool IsAddTime=true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime dt = DateTime.UtcNow;
|
||||||
|
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time");
|
||||||
|
|
||||||
|
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(dt, cstZone);
|
||||||
|
|
||||||
|
var folderNameByMonth = localTime.Year.ToString() + "-" + localTime.Month.ToString();
|
||||||
|
folderPath = Path.Combine(folderPath, folderNameByMonth);
|
||||||
|
if (!Directory.Exists(folderPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folderPath);
|
||||||
|
}
|
||||||
|
var filePath = Path.Combine(folderPath, fileName);
|
||||||
|
if(IsAddTime)
|
||||||
|
{
|
||||||
|
data = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss ") + data;
|
||||||
|
}
|
||||||
|
Log2File(filePath, data);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public static void LogException2File(string folderPath, string fileName,string exception)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tmpTime = DateTime.UtcNow.AddHours(7);
|
||||||
|
folderPath += tmpTime.Year.ToString() + "-" + tmpTime.Month.ToString();
|
||||||
|
|
||||||
|
string filePath = folderPath + "\\" + fileName + ".txt";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo info = new FileInfo(filePath);
|
||||||
|
if (info.Exists && info.Length > 10000000) // delete the file first if 10 MB
|
||||||
|
{
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
using var fs = File.Open(filePath, FileMode.Append);
|
||||||
|
using var sw = new StreamWriter(fs);
|
||||||
|
sw.WriteLine(exception);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double DistanceGpsCalculate(double lat1, double lon1, double lat2, double lon2, char unit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
double theta = lon1 - lon2;
|
||||||
|
double Dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) +
|
||||||
|
Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
|
||||||
|
Dist = Math.Acos(Dist);
|
||||||
|
Dist = rad2deg(Dist);
|
||||||
|
Dist = Dist * 60 * 1.1515;
|
||||||
|
if (unit == 'K')
|
||||||
|
{
|
||||||
|
Dist = Dist * 1.609344;
|
||||||
|
}
|
||||||
|
else if (unit == 'N')
|
||||||
|
{
|
||||||
|
Dist = Dist * 0.8684;
|
||||||
|
}
|
||||||
|
if (double.IsNaN(Dist))
|
||||||
|
return 0;
|
||||||
|
return Dist;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double deg2rad(double deg)
|
||||||
|
{
|
||||||
|
return (deg * Math.PI / 180.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double rad2deg(double rad)
|
||||||
|
{
|
||||||
|
return (rad / Math.PI * 180.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ConvertToHexaString(string strRFID)
|
||||||
|
{
|
||||||
|
string strResult = "";
|
||||||
|
int number = 0;
|
||||||
|
int.TryParse(strRFID, out number);
|
||||||
|
if (number != 0)
|
||||||
|
{
|
||||||
|
string hexaStr = number.ToString("X6");
|
||||||
|
string strNum1 = hexaStr.Substring(0, 2);
|
||||||
|
string strNum2 = hexaStr.Substring(2, 2);
|
||||||
|
string strNum3 = hexaStr.Substring(4, 2);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int num1 = int.Parse(strNum1, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
int num2 = int.Parse(strNum2, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
int num3 = int.Parse(strNum3, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
int num4 = num1 ^ num2 ^ num3;
|
||||||
|
|
||||||
|
strResult = hexaStr; // +num4.ToString("X2"); //bo thong tin checksum
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
strResult = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> ConvertRFIDStringToList(string RFIDString)
|
||||||
|
{
|
||||||
|
// checksum to get rfid
|
||||||
|
var rfidList = new List<string>();
|
||||||
|
int checksum = 0;
|
||||||
|
|
||||||
|
for (int k = 0; k < RFIDString.Length - 7; k++)
|
||||||
|
{
|
||||||
|
string strNum1 = RFIDString.Substring(k, 2);
|
||||||
|
string strNum2 = RFIDString.Substring(k + 2, 2);
|
||||||
|
string strNum3 = RFIDString.Substring(k + 4, 2);
|
||||||
|
string strNum4 = RFIDString.Substring(k + 6, 2);
|
||||||
|
|
||||||
|
// loai bo cac RFID ffffffff và ffffff01
|
||||||
|
if (strNum1 == "ff" && strNum2 == "ff")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int num1 = int.Parse(strNum1, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
int num2 = int.Parse(strNum2, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
int num3 = int.Parse(strNum3, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
checksum = num1 ^ num2 ^ num3;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int num4 = int.Parse(strNum4, System.Globalization.NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
if (checksum == num4)
|
||||||
|
{
|
||||||
|
string rfid_hex = strNum1 + strNum2 + strNum3;
|
||||||
|
rfidList.Add(rfid_hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rfidList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetString(byte[] buffer, int index, int count)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var enc = new UTF8Encoding();
|
||||||
|
if (index + count > buffer.Length) return "";
|
||||||
|
var i = 0;
|
||||||
|
var temp = "";
|
||||||
|
while (i < count)
|
||||||
|
{
|
||||||
|
if (buffer[index + i] == '\0')
|
||||||
|
break;
|
||||||
|
temp += enc.GetString(buffer, index + i, 1);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return temp.ToLower();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
RuleEngine/Constants/CommandCode.cs
Normal file
7
RuleEngine/Constants/CommandCode.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace RuleEngine.Constants
|
||||||
|
{
|
||||||
|
public class CommandCode
|
||||||
|
{
|
||||||
|
public const string UpdatePVT = "UPDATE_PVT";
|
||||||
|
}
|
||||||
|
}
|
9
RuleEngine/Constants/DeviceConfig.cs
Normal file
9
RuleEngine/Constants/DeviceConfig.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace RuleEngine.Constants
|
||||||
|
{
|
||||||
|
public static class DeviceConfig
|
||||||
|
{
|
||||||
|
public const double SPOOFING_THRESH_DISTANCE = 10;
|
||||||
|
public const double STOP_SPEED_LIMIT = 15;
|
||||||
|
public const double SECONDS_SET_CAR_STOP = 900; // 15 phút
|
||||||
|
}
|
||||||
|
}
|
14
RuleEngine/Constants/LogConstants.cs
Normal file
14
RuleEngine/Constants/LogConstants.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace RuleEngine.Constants
|
||||||
|
{
|
||||||
|
public static class LogConstants
|
||||||
|
{
|
||||||
|
public const string LOG_FILE_PATH = "../Logs/";
|
||||||
|
public const string LOG_EXCEPTION_FILE_PATH = "../Exceptions/";
|
||||||
|
public const string LogFileNameError = "RuleEngineErrorLog.txt";
|
||||||
|
public const string LogFileNamePVT = "PVTLog.txt";
|
||||||
|
public const string LogFileNameCommand = "CommandLog.txt";
|
||||||
|
public const string LogFileNameDevice = "DeviceLog.txt";
|
||||||
|
public const string LogFileNameCarHistory = "CarHistoryLog.txt";
|
||||||
|
public const string LogFileNameCarHistoryError = "CarHistoryErrorLog.txt";
|
||||||
|
}
|
||||||
|
}
|
8
RuleEngine/Constants/MqttTopic.cs
Normal file
8
RuleEngine/Constants/MqttTopic.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace RuleEngine.Constants
|
||||||
|
{
|
||||||
|
public class MqttTopic
|
||||||
|
{
|
||||||
|
public const string UpdatePVTFormat = "devices/update_pvt/{0}";
|
||||||
|
public const string UpdatePVTResponseFormat = "devices/update_pvt_response/{0}";
|
||||||
|
}
|
||||||
|
}
|
7
RuleEngine/Constants/QueueConstant.cs
Normal file
7
RuleEngine/Constants/QueueConstant.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace RuleEngine.Constants
|
||||||
|
{
|
||||||
|
public static class QueueConstant
|
||||||
|
{
|
||||||
|
public const int QUEUESIZE = 1024 * 8; // 8MB
|
||||||
|
}
|
||||||
|
}
|
52
RuleEngine/DTOs/DeviceMessage.cs
Normal file
52
RuleEngine/DTOs/DeviceMessage.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
namespace RuleEngine.DTOs
|
||||||
|
{
|
||||||
|
public class DeviceMessage
|
||||||
|
{
|
||||||
|
public string? Imei { get; set; }
|
||||||
|
public string? ReceivedTime { get; set; }
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
public int GpsSpeed { get; set; }
|
||||||
|
public string? CelID { get; set; }
|
||||||
|
public string? LacID { get; set; }
|
||||||
|
public bool IsSOS { get; set; }
|
||||||
|
public bool IsStrongBoxOpen { get; set; }
|
||||||
|
public bool IsEngineOn { get; set; }
|
||||||
|
public bool IsStopping { get; set; }
|
||||||
|
public bool IsGPSLost { get; set; }
|
||||||
|
public int TotalImgCam1 { get; set; }
|
||||||
|
public int TotalImgCam2 { get; set; }
|
||||||
|
public string? StrRFID { get; set; }
|
||||||
|
public string? StrOBD { get; set; }
|
||||||
|
public string? Provider { get; set; }
|
||||||
|
public string? Version { get; set; }
|
||||||
|
public int CpuTime { get; set; }
|
||||||
|
public int GeoMinDistance { get; set; }
|
||||||
|
public double NearestLat { get; set; }
|
||||||
|
public double NearestLon { get; set; }
|
||||||
|
public int NearestID { get; set; }
|
||||||
|
public string? NearestName { get; set; }
|
||||||
|
public int Maxsnr { get; set; }
|
||||||
|
public string? CpuTemp { get; set; }
|
||||||
|
public GpsInfor? GpsInfor { get; set; }
|
||||||
|
public bool Cam1OK { get; set; }
|
||||||
|
public bool Cam2OK { get; set; }
|
||||||
|
public bool CamDetach { get; set; }
|
||||||
|
public bool IsJamming { get; set; }
|
||||||
|
public bool IsSpoofing { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GpsInfor
|
||||||
|
{
|
||||||
|
public double AgcLevelDb { get; set; }
|
||||||
|
public double Maxsnr { get; set; }
|
||||||
|
public int SatelliteUseInFixed { get; set; }
|
||||||
|
public string? Cellid { get; set; }
|
||||||
|
public string? Lacid { get; set; }
|
||||||
|
public string? Mcc { get; set; }
|
||||||
|
public string? Mnc { get; set; }
|
||||||
|
public string? NetworkType { get; set; }
|
||||||
|
public string? SignalStrength { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
85
RuleEngine/Database/DatabaseContext.cs
Normal file
85
RuleEngine/Database/DatabaseContext.cs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using RuleEngine.Models;
|
||||||
|
|
||||||
|
namespace RuleEngine.Database
|
||||||
|
{
|
||||||
|
public partial class DatabaseContext(DbContextOptions<DatabaseContext> options) : DbContext(options)
|
||||||
|
{
|
||||||
|
public virtual DbSet<AppVersion> AppVersion { get; set; }
|
||||||
|
public virtual DbSet<Atmtechnican> Atmtechnicans { get; set; }
|
||||||
|
public virtual DbSet<Car> Cars { get; set; }
|
||||||
|
public virtual DbSet<DailyCar> DailyCars { get; set; }
|
||||||
|
public virtual DbSet<DailyKmCar> DailyKmCars { get; set; }
|
||||||
|
public virtual DbSet<Device> Devices { get; set; }
|
||||||
|
public virtual DbSet<Driver> Drivers { get; set; }
|
||||||
|
public virtual DbSet<Escort> Escorts { get; set; }
|
||||||
|
public virtual DbSet<History> Histories { get; set; }
|
||||||
|
public virtual DbSet<Online> Onlines { get; set; }
|
||||||
|
public virtual DbSet<Permission> Permissions { get; set; }
|
||||||
|
public virtual DbSet<Rfid> Rfids { get; set; }
|
||||||
|
public virtual DbSet<Scope> Scopes { get; set; }
|
||||||
|
public virtual DbSet<ScopePermission> ScopePermissions { get; set; }
|
||||||
|
public virtual DbSet<TransactionPoint> TransactionPoints { get; set; }
|
||||||
|
public virtual DbSet<Treasurer> Treasurers { get; set; }
|
||||||
|
public virtual DbSet<Unit> Units { get; set; }
|
||||||
|
public virtual DbSet<User> Users { get; set; }
|
||||||
|
public virtual DbSet<UserUnit> UserUnits { get; set; }
|
||||||
|
public virtual DbSet<Recipient> Recipients { get; set; }
|
||||||
|
public virtual DbSet<RecipientWarnCfg> RecipientWarnCfgs { get; set; }
|
||||||
|
public virtual DbSet<WarningEvent> WarningEvents { get; set; }
|
||||||
|
public virtual DbSet<WarningType> WarningTypes { get; set; }
|
||||||
|
public virtual DbSet<WarningSms> WarningSms { get; set; }
|
||||||
|
public virtual DbSet<AppCfg> AppCfgs { get; set; }
|
||||||
|
public virtual DbSet<Camera> Cameras { get; set; }
|
||||||
|
public virtual DbSet<ReupLog> ReupLogs { get; set; }
|
||||||
|
public virtual DbSet<CellInfor> CellInfors { get; set; }
|
||||||
|
public virtual DbSet<Schedule> Schedules { get; set; }
|
||||||
|
public virtual DbSet<Owner> Owners { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Schedule>(entity =>
|
||||||
|
{
|
||||||
|
entity.ToTable("Schedule");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<Owner>(entity =>
|
||||||
|
{
|
||||||
|
entity.ToTable("Owner");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<Device>(entity =>
|
||||||
|
{
|
||||||
|
entity.Property(e => e.ActivationTime).HasColumnType("date");
|
||||||
|
|
||||||
|
entity.Property(e => e.AllowUpdate).HasDefaultValueSql("((1))");
|
||||||
|
|
||||||
|
entity.Property(e => e.CarId).HasColumnName("CarId");
|
||||||
|
|
||||||
|
entity.Property(e => e.DeviceNumber)
|
||||||
|
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.IsUnicode(false);
|
||||||
|
|
||||||
|
entity.Property(e => e.Imei)
|
||||||
|
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.IsUnicode(false)
|
||||||
|
.HasColumnName("IMEI");
|
||||||
|
|
||||||
|
entity.Property(e => e.Phone)
|
||||||
|
|
||||||
|
.HasMaxLength(15)
|
||||||
|
.IsUnicode(false);
|
||||||
|
|
||||||
|
entity.Property(e => e.IsActive).HasDefaultValueSql("((1))");
|
||||||
|
|
||||||
|
entity.Property(e => e.UnitId).HasColumnName("UnitId");
|
||||||
|
});
|
||||||
|
|
||||||
|
OnModelCreatingPartial(modelBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||||
|
}
|
||||||
|
}
|
26
RuleEngine/Dockerfile
Normal file
26
RuleEngine/Dockerfile
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||||
|
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
|
||||||
|
USER $APP_UID
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# This stage is used to build the service project
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["RuleEngine.csproj", "./"]
|
||||||
|
RUN dotnet restore "./RuleEngine.csproj"
|
||||||
|
COPY . .
|
||||||
|
RUN dotnet build "./RuleEngine.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
# This stage is used to publish the service project to be copied to the final stage
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./RuleEngine.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "RuleEngine.dll"]
|
9
RuleEngine/Interfaces/IDeviceService.cs
Normal file
9
RuleEngine/Interfaces/IDeviceService.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using RuleEngine.DTOs;
|
||||||
|
|
||||||
|
namespace RuleEngine.Interfaces
|
||||||
|
{
|
||||||
|
public interface IDeviceService
|
||||||
|
{
|
||||||
|
Task<bool> UpdatePVT(DeviceMessage deviceMessage);
|
||||||
|
}
|
||||||
|
}
|
133
RuleEngine/Logs/ruleengine_log_20250514.log
Normal file
133
RuleEngine/Logs/ruleengine_log_20250514.log
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
2025-05-14 09:17:12.740 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:12.7276706+07:00"
|
||||||
|
2025-05-14 09:17:13.796 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:13.7961081+07:00"
|
||||||
|
2025-05-14 09:17:14.796 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:14.7962902+07:00"
|
||||||
|
2025-05-14 09:17:15.811 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:15.8117548+07:00"
|
||||||
|
2025-05-14 09:17:16.820 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:16.8206737+07:00"
|
||||||
|
2025-05-14 09:17:17.827 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:17.8269688+07:00"
|
||||||
|
2025-05-14 09:17:18.828 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:18.8278520+07:00"
|
||||||
|
2025-05-14 09:17:19.828 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:19.8287620+07:00"
|
||||||
|
2025-05-14 09:17:20.838 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:20.8383296+07:00"
|
||||||
|
2025-05-14 09:17:21.841 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:21.8409546+07:00"
|
||||||
|
2025-05-14 09:17:22.853 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:22.8529941+07:00"
|
||||||
|
2025-05-14 09:17:23.868 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:23.8684148+07:00"
|
||||||
|
2025-05-14 09:17:24.882 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:24.8820786+07:00"
|
||||||
|
2025-05-14 09:17:25.883 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:25.8837286+07:00"
|
||||||
|
2025-05-14 09:17:26.885 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:26.8851262+07:00"
|
||||||
|
2025-05-14 09:17:27.892 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:27.8922497+07:00"
|
||||||
|
2025-05-14 09:17:28.905 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:28.9051632+07:00"
|
||||||
|
2025-05-14 09:17:29.916 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:29.9163686+07:00"
|
||||||
|
2025-05-14 09:17:30.922 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:30.9224367+07:00"
|
||||||
|
2025-05-14 09:17:31.937 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:31.9374909+07:00"
|
||||||
|
2025-05-14 09:17:32.968 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:32.9685765+07:00"
|
||||||
|
2025-05-14 09:17:33.970 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:33.9703344+07:00"
|
||||||
|
2025-05-14 09:17:34.979 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:34.9797880+07:00"
|
||||||
|
2025-05-14 09:17:35.988 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:35.9885948+07:00"
|
||||||
|
2025-05-14 09:17:36.995 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:36.9958101+07:00"
|
||||||
|
2025-05-14 09:17:38.002 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:38.0021677+07:00"
|
||||||
|
2025-05-14 09:17:39.004 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:39.0046798+07:00"
|
||||||
|
2025-05-14 09:17:40.005 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:40.0058954+07:00"
|
||||||
|
2025-05-14 09:17:41.013 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:41.0129768+07:00"
|
||||||
|
2025-05-14 09:17:42.027 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:42.0270897+07:00"
|
||||||
|
2025-05-14 09:17:43.030 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:43.0305310+07:00"
|
||||||
|
2025-05-14 09:17:44.046 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:44.0465228+07:00"
|
||||||
|
2025-05-14 09:17:45.047 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:45.0477713+07:00"
|
||||||
|
2025-05-14 09:17:46.057 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:46.0577440+07:00"
|
||||||
|
2025-05-14 09:17:47.061 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:47.0617174+07:00"
|
||||||
|
2025-05-14 09:17:48.076 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:48.0765205+07:00"
|
||||||
|
2025-05-14 09:17:49.089 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:49.0898046+07:00"
|
||||||
|
2025-05-14 09:17:50.092 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:50.0924799+07:00"
|
||||||
|
2025-05-14 09:17:51.092 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:51.0926721+07:00"
|
||||||
|
2025-05-14 09:17:52.095 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:52.0952919+07:00"
|
||||||
|
2025-05-14 09:17:53.102 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:53.1021814+07:00"
|
||||||
|
2025-05-14 09:17:54.104 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:54.1046435+07:00"
|
||||||
|
2025-05-14 09:17:55.119 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:55.1193882+07:00"
|
||||||
|
2025-05-14 09:17:56.126 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:56.1263659+07:00"
|
||||||
|
2025-05-14 09:17:57.133 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:57.1331774+07:00"
|
||||||
|
2025-05-14 09:17:58.141 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:58.1416681+07:00"
|
||||||
|
2025-05-14 09:17:59.151 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:17:59.1516672+07:00"
|
||||||
|
2025-05-14 09:18:00.165 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:00.1654727+07:00"
|
||||||
|
2025-05-14 09:18:01.168 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:01.1683941+07:00"
|
||||||
|
2025-05-14 09:18:02.177 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:02.1771225+07:00"
|
||||||
|
2025-05-14 09:18:03.192 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:03.1928842+07:00"
|
||||||
|
2025-05-14 09:18:04.199 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:04.1998288+07:00"
|
||||||
|
2025-05-14 09:18:05.202 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:05.2023057+07:00"
|
||||||
|
2025-05-14 09:18:06.217 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:06.2171677+07:00"
|
||||||
|
2025-05-14 09:18:07.217 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:07.2179408+07:00"
|
||||||
|
2025-05-14 09:18:08.227 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:08.2275777+07:00"
|
||||||
|
2025-05-14 09:18:09.228 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:09.2280441+07:00"
|
||||||
|
2025-05-14 09:18:10.243 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:10.2436759+07:00"
|
||||||
|
2025-05-14 09:18:11.248 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:11.2484326+07:00"
|
||||||
|
2025-05-14 09:18:12.263 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:12.2629629+07:00"
|
||||||
|
2025-05-14 09:18:13.278 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:13.2783009+07:00"
|
||||||
|
2025-05-14 09:18:14.293 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:14.2935860+07:00"
|
||||||
|
2025-05-14 09:18:15.294 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:15.2947761+07:00"
|
||||||
|
2025-05-14 09:18:16.294 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:16.2948210+07:00"
|
||||||
|
2025-05-14 09:18:17.296 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:17.2963056+07:00"
|
||||||
|
2025-05-14 09:18:18.309 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:18.3095020+07:00"
|
||||||
|
2025-05-14 09:18:19.323 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:19.3236472+07:00"
|
||||||
|
2025-05-14 09:18:20.335 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:20.3350007+07:00"
|
||||||
|
2025-05-14 09:18:21.342 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:21.3429593+07:00"
|
||||||
|
2025-05-14 09:18:22.343 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:22.3437814+07:00"
|
||||||
|
2025-05-14 09:18:23.355 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:23.3552780+07:00"
|
||||||
|
2025-05-14 09:18:24.369 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:24.3698567+07:00"
|
||||||
|
2025-05-14 09:18:25.371 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:25.3715111+07:00"
|
||||||
|
2025-05-14 09:18:26.372 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:26.3720109+07:00"
|
||||||
|
2025-05-14 09:18:27.389 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:27.3889656+07:00"
|
||||||
|
2025-05-14 09:18:28.395 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:28.3952182+07:00"
|
||||||
|
2025-05-14 09:18:29.404 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:29.4043060+07:00"
|
||||||
|
2025-05-14 09:18:30.404 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:30.4046716+07:00"
|
||||||
|
2025-05-14 09:18:31.419 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:31.4198009+07:00"
|
||||||
|
2025-05-14 09:18:32.426 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:32.4261075+07:00"
|
||||||
|
2025-05-14 09:18:33.426 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:33.4266313+07:00"
|
||||||
|
2025-05-14 09:18:34.441 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:34.4415592+07:00"
|
||||||
|
2025-05-14 09:18:35.449 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:35.4495647+07:00"
|
||||||
|
2025-05-14 09:18:36.451 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:36.4514174+07:00"
|
||||||
|
2025-05-14 09:18:37.466 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:37.4666474+07:00"
|
||||||
|
2025-05-14 09:18:38.467 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:38.4676530+07:00"
|
||||||
|
2025-05-14 09:18:39.482 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:39.4825603+07:00"
|
||||||
|
2025-05-14 09:18:40.487 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:40.4869981+07:00"
|
||||||
|
2025-05-14 09:18:41.489 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:41.4896773+07:00"
|
||||||
|
2025-05-14 09:18:42.499 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:42.4990012+07:00"
|
||||||
|
2025-05-14 09:18:43.514 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:43.5148356+07:00"
|
||||||
|
2025-05-14 09:18:44.515 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:44.5157501+07:00"
|
||||||
|
2025-05-14 09:18:45.530 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:45.5306296+07:00"
|
||||||
|
2025-05-14 09:18:46.542 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:46.5426243+07:00"
|
||||||
|
2025-05-14 09:18:47.557 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:47.5575964+07:00"
|
||||||
|
2025-05-14 09:18:48.560 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:48.5600813+07:00"
|
||||||
|
2025-05-14 09:18:49.560 +07:00 [INF] RuleEngineWorker running at: "2025-05-14T09:18:49.5602129+07:00"
|
||||||
|
2025-05-14 09:48:12.509 +07:00 [INF] RuleEngineWorker initialized with broker: 127.0.0.1:1883
|
||||||
|
2025-05-14 09:48:12.565 +07:00 [INF] RuleEngineWorker starting at: "2025-05-14T09:48:12.5648048+07:00"
|
||||||
|
2025-05-14 09:48:12.572 +07:00 [INF] Connecting to MQTT broker at 127.0.0.1:1883...
|
||||||
|
2025-05-14 09:48:12.695 +07:00 [INF] Connected to MQTT broker successfully
|
||||||
|
2025-05-14 09:48:12.696 +07:00 [INF] Subscribing to MQTT topics...
|
||||||
|
2025-05-14 09:48:12.729 +07:00 [INF] Subscribed to topic 'iot/devices/#' with ResultCode: "GrantedQoS1"
|
||||||
|
2025-05-14 09:48:12.730 +07:00 [INF] MQTT reconnect timer initialized and started
|
||||||
|
2025-05-14 09:50:25.389 +07:00 [INF] Stopping RuleEngineWorker
|
||||||
|
2025-05-14 09:50:25.436 +07:00 [INF] Disconnected from MQTT broker
|
||||||
|
2025-05-14 09:50:25.439 +07:00 [INF] RuleEngineWorker stopping due to cancellation
|
||||||
|
2025-05-14 09:50:25.441 +07:00 [WRN] Disconnected from MQTT broker: "NormalDisconnection"
|
||||||
|
2025-05-14 09:50:25.441 +07:00 [INF] RuleEngineWorker stopped at: "2025-05-14T09:50:25.4392784+07:00"
|
||||||
|
2025-05-14 09:50:38.767 +07:00 [INF] RuleEngineWorker initialized with broker: 127.0.0.1:1883
|
||||||
|
2025-05-14 09:50:38.824 +07:00 [INF] RuleEngineWorker starting at: "2025-05-14T09:50:38.8240853+07:00"
|
||||||
|
2025-05-14 09:50:38.832 +07:00 [INF] Connecting to MQTT broker at 127.0.0.1:1883...
|
||||||
|
2025-05-14 09:50:38.908 +07:00 [INF] Connected to MQTT broker successfully
|
||||||
|
2025-05-14 09:50:38.909 +07:00 [INF] Subscribing to MQTT topics...
|
||||||
|
2025-05-14 09:50:38.931 +07:00 [INF] Subscribed to topic 'iot/devices/#' with ResultCode: "GrantedQoS1"
|
||||||
|
2025-05-14 09:50:38.932 +07:00 [INF] MQTT reconnect timer initialized and started
|
||||||
|
2025-05-14 09:53:57.116 +07:00 [INF] Stopping RuleEngineWorker
|
||||||
|
2025-05-14 09:53:57.195 +07:00 [INF] Disconnected from MQTT broker
|
||||||
|
2025-05-14 09:53:57.198 +07:00 [INF] RuleEngineWorker stopping due to cancellation
|
||||||
|
2025-05-14 09:53:57.201 +07:00 [INF] RuleEngineWorker stopped at: "2025-05-14T09:53:57.1988118+07:00"
|
||||||
|
2025-05-14 09:53:57.201 +07:00 [WRN] Disconnected from MQTT broker: "NormalDisconnection"
|
||||||
|
2025-05-14 22:33:34.714 +07:00 [INF] RuleEngineWorker initialized with broker: localhost:1883
|
||||||
|
2025-05-14 22:33:34.795 +07:00 [INF] RuleEngineWorker starting at: "2025-05-14T22:33:34.7949053+07:00"
|
||||||
|
2025-05-14 22:33:34.803 +07:00 [INF] Connecting to MQTT broker at localhost:1883...
|
||||||
|
2025-05-14 22:33:35.014 +07:00 [INF] Connected to MQTT broker successfully
|
||||||
|
2025-05-14 22:33:35.015 +07:00 [INF] Subscribing to MQTT topics...
|
||||||
|
2025-05-14 22:33:35.048 +07:00 [INF] Subscribed to topic 'iot/devices/#' with ResultCode: "GrantedQoS1"
|
||||||
|
2025-05-14 22:33:35.050 +07:00 [INF] MQTT reconnect timer initialized and started
|
||||||
|
2025-05-14 22:34:05.777 +07:00 [INF] Stopping RuleEngineWorker
|
||||||
|
2025-05-14 22:34:05.790 +07:00 [INF] Disconnected from MQTT broker
|
||||||
|
2025-05-14 22:34:05.791 +07:00 [INF] RuleEngineWorker stopping due to cancellation
|
||||||
|
2025-05-14 22:34:05.792 +07:00 [INF] RuleEngineWorker stopped at: "2025-05-14T22:34:05.7919355+07:00"
|
||||||
|
2025-05-14 22:34:05.793 +07:00 [WRN] Disconnected from MQTT broker: "NormalDisconnection"
|
18
RuleEngine/Models/AppCfg.cs
Normal file
18
RuleEngine/Models/AppCfg.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class AppCfg
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string CfgKey { get; set; }
|
||||||
|
public string CfgName { get; set; }
|
||||||
|
public double? CfgValue { get; set; }
|
||||||
|
public string CfgStrValue { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
}
|
||||||
|
}
|
16
RuleEngine/Models/AppVersion.cs
Normal file
16
RuleEngine/Models/AppVersion.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class AppVersion
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public int? Version { get; set; }
|
||||||
|
public string FolderPath { get; set; }
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
21
RuleEngine/Models/Atmtechnican.cs
Normal file
21
RuleEngine/Models/Atmtechnican.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Atmtechnican
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public string EmployeeCode { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Sex { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool? Status { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? Rfidid { get; set; }
|
||||||
|
}
|
||||||
|
}
|
18
RuleEngine/Models/Camera.cs
Normal file
18
RuleEngine/Models/Camera.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Camera
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? CamNo { get; set; }
|
||||||
|
public string CamImgPath { get; set; }
|
||||||
|
public DateTime? ReceivedTime { get; set; }
|
||||||
|
public DateTime? DeviceTime { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
public int? DeviceDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
39
RuleEngine/Models/Car.cs
Normal file
39
RuleEngine/Models/Car.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Car
|
||||||
|
{
|
||||||
|
public Car()
|
||||||
|
{
|
||||||
|
DailyCars = new HashSet<DailyCar>();
|
||||||
|
DailyKmCars = new HashSet<DailyKmCar>();
|
||||||
|
Devices = new HashSet<Device>();
|
||||||
|
Histories = new HashSet<History>();
|
||||||
|
Onlines = new HashSet<Online>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string LicensePlate { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public int NumberCamera { get; set; }
|
||||||
|
public int? FirstCamPo { get; set; }
|
||||||
|
public int? FirstCamRotation { get; set; }
|
||||||
|
public int? SecondCamRotation { get; set; }
|
||||||
|
public int? Fuel { get; set; }
|
||||||
|
public int? LimitedSpeed { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? RfidId { get; set; }
|
||||||
|
public int? DriverId { get; set; }
|
||||||
|
public int? DeviceId { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<DailyCar> DailyCars { get; set; }
|
||||||
|
public virtual ICollection<DailyKmCar> DailyKmCars { get; set; }
|
||||||
|
public virtual ICollection<Device> Devices { get; set; }
|
||||||
|
public virtual ICollection<History> Histories { get; set; }
|
||||||
|
public virtual ICollection<Online> Onlines { get; set; }
|
||||||
|
}
|
||||||
|
}
|
19
RuleEngine/Models/CellInfor.cs
Normal file
19
RuleEngine/Models/CellInfor.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class CellInfor
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? MNC { get; set; }
|
||||||
|
public int? MCC { get; set; }
|
||||||
|
public int? CellId { get; set; }
|
||||||
|
public int? LacId { get; set; }
|
||||||
|
public double? NetworkLat { get; set; }
|
||||||
|
public double? NetworkLon { get; set; }
|
||||||
|
public int? NetworkType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
28
RuleEngine/Models/DailyCar.cs
Normal file
28
RuleEngine/Models/DailyCar.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class DailyCar
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public double? TotalKm { get; set; }
|
||||||
|
public int? TotalFuel { get; set; }
|
||||||
|
public int? RunningTime { get; set; }
|
||||||
|
public int? OpenSafeBox { get; set; }
|
||||||
|
public int? Conflict { get; set; }
|
||||||
|
public int? SegmentationId { get; set; }
|
||||||
|
public int? RouteDeviation { get; set; }
|
||||||
|
public int? TimeDeviation { get; set; }
|
||||||
|
public DateTime? ReportTime { get; set; }
|
||||||
|
public string CarLicensePlate { get; set; }
|
||||||
|
public string RouteCode { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
public int? RouteId { get; set; }
|
||||||
|
|
||||||
|
public virtual Car Car { get; set; }
|
||||||
|
}
|
||||||
|
}
|
23
RuleEngine/Models/DailyKmCar.cs
Normal file
23
RuleEngine/Models/DailyKmCar.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class DailyKmCar
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Driver { get; set; }
|
||||||
|
public string Treasure { get; set; }
|
||||||
|
public double? TotalKm { get; set; }
|
||||||
|
public DateTime? ReportTime { get; set; }
|
||||||
|
public string UnitName { get; set; }
|
||||||
|
public string CarLicensePlate { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
|
||||||
|
public virtual Car Car { get; set; }
|
||||||
|
}
|
||||||
|
}
|
25
RuleEngine/Models/Device.cs
Normal file
25
RuleEngine/Models/Device.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Device
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string DeviceNumber { get; set; }
|
||||||
|
public string Imei { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public int CarrierId { get; set; }
|
||||||
|
public DateTime? ActivationTime { get; set; }
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
public bool? AllowUpdate { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
|
||||||
|
public int? FanStatus { get; set; }
|
||||||
|
public string ServerUrl { get; set; }
|
||||||
|
// public virtual Car Car { get; set; }
|
||||||
|
}
|
||||||
|
}
|
21
RuleEngine/Models/Driver.cs
Normal file
21
RuleEngine/Models/Driver.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Driver
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public string EmployeeCode { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Sex { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool? Status { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? Rfidid { get; set; }
|
||||||
|
}
|
||||||
|
}
|
21
RuleEngine/Models/Escort.cs
Normal file
21
RuleEngine/Models/Escort.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Escort
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public string EmployeeCode { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Sex { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool Status { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? Rfidid { get; set; }
|
||||||
|
}
|
||||||
|
}
|
36
RuleEngine/Models/History.cs
Normal file
36
RuleEngine/Models/History.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class History
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Rfidstring { get; set; }
|
||||||
|
public DateTime? ReceivedTime { get; set; }
|
||||||
|
public DateTime? DeviceTime { get; set; }
|
||||||
|
public int? CarStatus { get; set; }
|
||||||
|
public bool? EngineOn { get; set; }
|
||||||
|
public bool? StrongBoxOpen { get; set; }
|
||||||
|
public bool? IsSos { get; set; }
|
||||||
|
public bool? IsGPSLost { get; set; }
|
||||||
|
public double? GpsLat { get; set; }
|
||||||
|
public double? GpsLon { get; set; }
|
||||||
|
public double? NetworkLat { get; set; }
|
||||||
|
public double? NetworkLon { get; set; }
|
||||||
|
public int? GpsVelocity { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
public int? DeviceDate { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public virtual Car Car { get; set; }
|
||||||
|
public string GpsInfor { get; internal set; }
|
||||||
|
public bool IsJamming { get; internal set; }
|
||||||
|
public bool IsSpoofing { get; internal set; }
|
||||||
|
public bool Cam1OK { get; internal set; }
|
||||||
|
public bool Cam2OK { get; internal set; }
|
||||||
|
public bool CamDetach { get; internal set; }
|
||||||
|
}
|
||||||
|
}
|
38
RuleEngine/Models/Online.cs
Normal file
38
RuleEngine/Models/Online.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Online
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Rfidstring { get; set; }
|
||||||
|
public string AppVersion { get; set; }
|
||||||
|
public DateTime? ReceivedTime { get; set; }
|
||||||
|
public DateTime? DeviceTime { get; set; }
|
||||||
|
public int? CarStatus { get; set; }
|
||||||
|
public bool? EngineOn { get; set; }
|
||||||
|
public bool? StrongBoxOpen { get; set; }
|
||||||
|
public bool? IsSos { get; set; }
|
||||||
|
public bool? IsGPSLost { get; set; }
|
||||||
|
public double? GpsLat { get; set; }
|
||||||
|
public double? GpsLon { get; set; }
|
||||||
|
public string GpsInfor { get; set; }
|
||||||
|
public double? NetworkLat { get; set; }
|
||||||
|
public double? NetworkLon { get; set; }
|
||||||
|
public bool? IsJamming { get; set; }
|
||||||
|
public bool? IsSpoofing { get; set; }
|
||||||
|
|
||||||
|
public int? GpsVelocity { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
public int? CellId { get; set; }
|
||||||
|
public int? LacId { get; set; }
|
||||||
|
|
||||||
|
public virtual Car Car { get; set; }
|
||||||
|
public bool IsCam1OK { get; internal set; }
|
||||||
|
public bool IsCam2OK { get; internal set; }
|
||||||
|
}
|
||||||
|
}
|
21
RuleEngine/Models/Owner.cs
Normal file
21
RuleEngine/Models/Owner.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Owner
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public string EmployeeCode { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int? Sex { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool? Status { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? RfidId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
26
RuleEngine/Models/Permission.cs
Normal file
26
RuleEngine/Models/Permission.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Permission
|
||||||
|
{
|
||||||
|
public Permission()
|
||||||
|
{
|
||||||
|
ScopePermissions = new HashSet<ScopePermission>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Resource { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string Method { get; set; }
|
||||||
|
public string Action { get; set; }
|
||||||
|
public string Filter { get; set; }
|
||||||
|
public string FilterValue { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<ScopePermission> ScopePermissions { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
RuleEngine/Models/Recipient.cs
Normal file
17
RuleEngine/Models/Recipient.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Recipient
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string StaffCode { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Position { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
}
|
||||||
|
}
|
15
RuleEngine/Models/RecipientWarnCfg.cs
Normal file
15
RuleEngine/Models/RecipientWarnCfg.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class RecipientWarnCfg
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? RecipientId { get; set; }
|
||||||
|
public int? WarningTypeId { get; set; }
|
||||||
|
public bool? IsSendSms { get; set; }
|
||||||
|
public bool? IsSendEmail { get; set; }
|
||||||
|
public bool? IsEnable { get; set; }
|
||||||
|
}
|
||||||
|
}
|
20
RuleEngine/Models/ReupLogs.cs
Normal file
20
RuleEngine/Models/ReupLogs.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class ReupLog
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string IMEI { get; set; }
|
||||||
|
public DateTime? ReceivedTime { get; set; }
|
||||||
|
public DateTime? FirstRecordTime { get; set; }
|
||||||
|
public DateTime? LastRecordTime { get; set; }
|
||||||
|
public int? FirstRecordId { get; set; }
|
||||||
|
public int? LastRecordId { get; set; }
|
||||||
|
public DateTime? DeviceDate { get; set; }
|
||||||
|
public int? RecordType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
26
RuleEngine/Models/Rfid.cs
Normal file
26
RuleEngine/Models/Rfid.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Rfid
|
||||||
|
{
|
||||||
|
public Rfid()
|
||||||
|
{
|
||||||
|
Treasurers = new HashSet<Treasurer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string CardNumber { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public DateTime? ActivationTime { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
|
public bool IsDistributed { get; set; }
|
||||||
|
public bool? Status { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<Treasurer> Treasurers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
44
RuleEngine/Models/Schedule.cs
Normal file
44
RuleEngine/Models/Schedule.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Schedule
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? SampleId { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
public string CarPlateNumber { get; set; }
|
||||||
|
public int? DriverId { get; set; }
|
||||||
|
public string DriverName { get; set; }
|
||||||
|
public int? OwnerId { get; set; }
|
||||||
|
public string OwnerName { get; set; }
|
||||||
|
public int? AtmTechnicanId { get; set; }
|
||||||
|
public string KtvAtmName { get; set; }
|
||||||
|
public int? EscortId { get; set; }
|
||||||
|
public string EscortName { get; set; }
|
||||||
|
public string SecurityName { get; set; }
|
||||||
|
public DateTime? AssignedDate { get; set; }
|
||||||
|
public DateTime? RunningDate { get; set; }
|
||||||
|
public DateTime? StartTime { get; set; }
|
||||||
|
public DateTime? EndTime { get; set; }
|
||||||
|
public string SampleCode { get; set; }
|
||||||
|
public string SampleName { get; set; }
|
||||||
|
public string ScheduleName { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public string LstPoint { get; set; }
|
||||||
|
public string LstTimeLabel { get; set; }
|
||||||
|
public string SampleDetail { get; set; }
|
||||||
|
public int? SampleType { get; set; }
|
||||||
|
public double? MinutesLateArrival { get; set; }
|
||||||
|
public double? AllowedRangeKm { get; set; }
|
||||||
|
public double? DistanceTmp { get; set; }
|
||||||
|
public double? TimeTmp { get; set; }
|
||||||
|
public bool? UnlimitedTime { get; set; }
|
||||||
|
public DateTime? AccessedAt { get; set; }
|
||||||
|
public DateTime? CreatedAt { get; set; }
|
||||||
|
public DateTime? DeletedAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
21
RuleEngine/Models/Scope.cs
Normal file
21
RuleEngine/Models/Scope.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Scope
|
||||||
|
{
|
||||||
|
public Scope()
|
||||||
|
{
|
||||||
|
Users = new HashSet<User>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string AllowedRoute { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<User> Users { get; set; }
|
||||||
|
}
|
||||||
|
}
|
18
RuleEngine/Models/ScopePermission.cs
Normal file
18
RuleEngine/Models/ScopePermission.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class ScopePermission
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Allowed { get; set; }
|
||||||
|
public string Filter { get; set; }
|
||||||
|
public int? PermissionId { get; set; }
|
||||||
|
public int? ScopeId { get; set; }
|
||||||
|
|
||||||
|
public virtual Permission Permission { get; set; }
|
||||||
|
}
|
||||||
|
}
|
24
RuleEngine/Models/TransactionPoint.cs
Normal file
24
RuleEngine/Models/TransactionPoint.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class TransactionPoint
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string PointCode { get; set; }
|
||||||
|
public string PointName { get; set; }
|
||||||
|
public int PointType { get; set; }
|
||||||
|
public string Address { get; set; }
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
public string Branch { get; set; }
|
||||||
|
public string Contact { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Fax { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public double? Distance { get; set; }
|
||||||
|
}
|
||||||
|
}
|
24
RuleEngine/Models/Treasurer.cs
Normal file
24
RuleEngine/Models/Treasurer.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Treasurer
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public string EmployeeCode { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Sex { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool? Status { get; set; }
|
||||||
|
public int? UnitId { get; set; }
|
||||||
|
public int? Rfidid { get; set; }
|
||||||
|
|
||||||
|
public virtual Rfid Rfid { get; set; }
|
||||||
|
}
|
||||||
|
}
|
24
RuleEngine/Models/Unit.cs
Normal file
24
RuleEngine/Models/Unit.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class Unit
|
||||||
|
{
|
||||||
|
public Unit()
|
||||||
|
{
|
||||||
|
UserUnits = new HashSet<UserUnit>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Province { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public DateTime? CreatedAt { get; set; }
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<UserUnit> UserUnits { get; set; }
|
||||||
|
}
|
||||||
|
}
|
19
RuleEngine/Models/User.cs
Normal file
19
RuleEngine/Models/User.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class User
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public bool? Status { get; set; }
|
||||||
|
public int? ScopeId { get; set; }
|
||||||
|
|
||||||
|
public virtual Scope Scope { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
RuleEngine/Models/UserUnit.cs
Normal file
17
RuleEngine/Models/UserUnit.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class UserUnit
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public int UnitId { get; set; }
|
||||||
|
|
||||||
|
public virtual Unit Unit { get; set; }
|
||||||
|
}
|
||||||
|
}
|
28
RuleEngine/Models/WarningEvent.cs
Normal file
28
RuleEngine/Models/WarningEvent.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class WarningEvent
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? WarningTypeId { get; set; }
|
||||||
|
public int? ScheduleId { get; set; }
|
||||||
|
public int? CarId { get; set; }
|
||||||
|
public double? StartLatitude { get; set; }
|
||||||
|
public double? StartLongitude { get; set; }
|
||||||
|
public double? EndLatitude { get; set; }
|
||||||
|
public double? EndLongitude { get; set; }
|
||||||
|
public DateTime? StartTime { get; set; }
|
||||||
|
public DateTime? EndTime { get; set; }
|
||||||
|
public string EventContent { get; set; }
|
||||||
|
public int? NearPointId { get; set; }
|
||||||
|
public string NearPointName { get; set; }
|
||||||
|
public double? NearPointDistance { get; set; }
|
||||||
|
public bool? StopSound { get; set; }
|
||||||
|
public string Reason { get; set; }
|
||||||
|
public DateTime? ReasonInputTime { get; set; }
|
||||||
|
}
|
||||||
|
}
|
18
RuleEngine/Models/WarningSms.cs
Normal file
18
RuleEngine/Models/WarningSms.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class WarningSms
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? WarningId { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public bool? IsSent { get; set; }
|
||||||
|
public string SmsContent { get; set; }
|
||||||
|
public DateTime? CreatedAt { get; set; }
|
||||||
|
public DateTime? SentAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
RuleEngine/Models/WarningType.cs
Normal file
17
RuleEngine/Models/WarningType.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RuleEngine.Models
|
||||||
|
{
|
||||||
|
public partial class WarningType
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string WarningName { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public int? WarningLevel { get; set; }
|
||||||
|
public bool? AllowSendEmail { get; set; }
|
||||||
|
public bool? AllowSendSms { get; set; }
|
||||||
|
}
|
||||||
|
}
|
39
RuleEngine/Program.cs
Normal file
39
RuleEngine/Program.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using RuleEngine.Database;
|
||||||
|
using RuleEngine.Interfaces;
|
||||||
|
using RuleEngine.Services;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace RuleEngine
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
|
// Cấu hình Serilog từ appsettings.json
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.ReadFrom.Configuration(builder.Configuration)
|
||||||
|
.CreateLogger();
|
||||||
|
// Gắn Serilog vào hệ thống logging
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
builder.Logging.AddSerilog(Log.Logger);
|
||||||
|
|
||||||
|
//Config cau hinh SqlServerConnection
|
||||||
|
builder.Services.AddDbContextFactory<DatabaseContext>(options =>
|
||||||
|
{
|
||||||
|
options.UseSqlServer(builder.Configuration.GetConnectionString("RuleEngineConnectStr"));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Thêm Rule Engine vào Background Service
|
||||||
|
builder.Services.AddHostedService<RuleEngineWorker>();
|
||||||
|
// Thêm các service hỗ trợ Rule Engine
|
||||||
|
builder.Services.AddScoped<IDeviceService, DeviceService>();
|
||||||
|
|
||||||
|
var host = builder.Build();
|
||||||
|
host.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
RuleEngine/Properties/launchSettings.json
Normal file
12
RuleEngine/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"RuleEngine": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"DOTNET_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
RuleEngine/Requests/DeviceLogRequest.cs
Normal file
34
RuleEngine/Requests/DeviceLogRequest.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
namespace RuleEngine.Requests
|
||||||
|
{
|
||||||
|
public class DeviceLogRequest
|
||||||
|
{
|
||||||
|
public int CarID { get; set; }
|
||||||
|
public required string Imei { get; set; }
|
||||||
|
public int UnitID { get; set; }
|
||||||
|
public DateTime ReceivedTime { get; set; }
|
||||||
|
public double? GpsLat { get; set; }
|
||||||
|
public double? GpsLon { get; set; }
|
||||||
|
public int? GpsSpeed { get; set; }
|
||||||
|
public int? CellID { get; set; }
|
||||||
|
public int? LacID { get; set; }
|
||||||
|
public bool? IsSos { get; set; }
|
||||||
|
public bool? IsStrongBoxOpen { get; set; }
|
||||||
|
public bool? IsEngineOn { get; set; }
|
||||||
|
public bool? IsGPSLost { get; set; }
|
||||||
|
public string? StrRFID { get; set; }
|
||||||
|
public double? OriLati { get; set; }
|
||||||
|
public double? OriLongi { get; set; }
|
||||||
|
public double? NetworkLat { get; set; }
|
||||||
|
public double? NetworkLon { get; set; }
|
||||||
|
public string? Version { get; set; }
|
||||||
|
public string? GPSInfor { get; set; }
|
||||||
|
public bool? IsJamming { get; set; }
|
||||||
|
public bool? IsSpoofing { get; set; }
|
||||||
|
public bool? Cam1OK { get; set; }
|
||||||
|
public bool? Cam2OK { get; set; }
|
||||||
|
public bool? CamDetach { get; set; }
|
||||||
|
public int? CarStatus { get; set; } // carStatus: 0 - running; 1 - stopping; 2 - stopping but not fix position
|
||||||
|
public string? Message { get; set; }
|
||||||
|
public required string Code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
23
RuleEngine/RuleEngine.csproj
Normal file
23
RuleEngine/RuleEngine.csproj
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>dotnet-RuleEngine-babf0c43-57b7-40b0-bbae-1a6b4a75f009</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
|
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
25
RuleEngine/RuleEngine.sln
Normal file
25
RuleEngine/RuleEngine.sln
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.13.35931.197 d17.13
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RuleEngine", "RuleEngine.csproj", "{FCC38139-19EB-472C-9D47-01F5960928AA}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FCC38139-19EB-472C-9D47-01F5960928AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FCC38139-19EB-472C-9D47-01F5960928AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FCC38139-19EB-472C-9D47-01F5960928AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FCC38139-19EB-472C-9D47-01F5960928AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {A03892B7-12D9-4E88-9EAF-244073E2B432}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
433
RuleEngine/RuleEngineWorker.cs
Normal file
433
RuleEngine/RuleEngineWorker.cs
Normal file
|
@ -0,0 +1,433 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RuleEngine.Constants;
|
||||||
|
using RuleEngine.Database;
|
||||||
|
using RuleEngine.DTOs;
|
||||||
|
using RuleEngine.Interfaces;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace RuleEngine
|
||||||
|
{
|
||||||
|
public class RuleEngineWorker : BackgroundService
|
||||||
|
{
|
||||||
|
// Configuration and dependency injection
|
||||||
|
private readonly ILogger<RuleEngineWorker> _logger;
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly IDbContextFactory<DatabaseContext> _dbContextFactory;
|
||||||
|
|
||||||
|
// MQTT client
|
||||||
|
private IMqttClient? _mqttClient;
|
||||||
|
private MqttClientOptions _mqttOptions;
|
||||||
|
private readonly SemaphoreSlim _mqttSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
// MQTT settings
|
||||||
|
private readonly string _mqttBroker;
|
||||||
|
private readonly int _mqttPort;
|
||||||
|
private readonly string _mqttUsername;
|
||||||
|
private readonly string _mqttPassword;
|
||||||
|
private readonly string _jwtSecret;
|
||||||
|
|
||||||
|
// Reconnection timer
|
||||||
|
private System.Timers.Timer _reconnectTimer = null!;
|
||||||
|
private bool _isDisposed = false;
|
||||||
|
|
||||||
|
public RuleEngineWorker(
|
||||||
|
ILogger<RuleEngineWorker> logger,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
IConfiguration configuration,
|
||||||
|
IDbContextFactory<DatabaseContext> dbContextFactory)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_config = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||||
|
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||||
|
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||||
|
|
||||||
|
// Load MQTT configuration
|
||||||
|
_mqttBroker = _config.GetSection("MqttSettings:Broker").Get<string>() ?? "localhost";
|
||||||
|
_mqttPort = _config.GetSection("MqttSettings:Port").Get<int?>() ?? 1883;
|
||||||
|
_jwtSecret = _config.GetSection("MqttSettings:JwtSecret").Get<string>() ?? "emqx@sivan";
|
||||||
|
_mqttUsername = _config.GetSection("MqttSettings:Username").Get<string>() ?? string.Empty;
|
||||||
|
_mqttPassword = _config.GetSection("MqttSettings:Password").Get<string>() ?? string.Empty;
|
||||||
|
|
||||||
|
// Create MQTT options
|
||||||
|
_mqttOptions = CreateMqttClientOptions();
|
||||||
|
|
||||||
|
_logger.LogInformation("RuleEngineWorker initialized with broker: {Broker}:{Port}", _mqttBroker, _mqttPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RuleEngineWorker starting at: {Time}", DateTimeOffset.Now);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await InitializeMqttClient();
|
||||||
|
InitializeReconnectTimer();
|
||||||
|
|
||||||
|
// Keep the service running
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("RuleEngineWorker running at: {Time}", DateTimeOffset.Now);
|
||||||
|
}
|
||||||
|
await Task.Delay(30000, stoppingToken); // Heartbeat every 30 seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RuleEngineWorker stopping due to cancellation");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unhandled exception in RuleEngineWorker");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await DisposeMqttClient();
|
||||||
|
_logger.LogInformation("RuleEngineWorker stopped at: {Time}", DateTimeOffset.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MqttClientOptions CreateMqttClientOptions()
|
||||||
|
{
|
||||||
|
return new MqttClientOptionsBuilder()
|
||||||
|
.WithTcpServer("localhost", 1883)
|
||||||
|
.WithClientId($"RuleEngineWorker-{Guid.NewGuid()}")
|
||||||
|
.WithCredentials(_mqttUsername, _mqttPassword)
|
||||||
|
.WithCleanSession(false)
|
||||||
|
.WithSessionExpiryInterval(604800) // 7 days in seconds
|
||||||
|
.WithKeepAlivePeriod(TimeSpan.FromSeconds(60))
|
||||||
|
.WithTimeout(TimeSpan.FromSeconds(15))
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeReconnectTimer()
|
||||||
|
{
|
||||||
|
_reconnectTimer = new System.Timers.Timer(10000); // 10 seconds
|
||||||
|
_reconnectTimer.Elapsed += async (sender, e) => await TryReconnectMqtt();
|
||||||
|
_reconnectTimer.AutoReset = true;
|
||||||
|
_reconnectTimer.Start();
|
||||||
|
|
||||||
|
_logger.LogInformation("MQTT reconnect timer initialized and started");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeMqttClient()
|
||||||
|
{
|
||||||
|
await _mqttSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_mqttClient = new MqttClientFactory().CreateMqttClient();
|
||||||
|
|
||||||
|
// Set up event handlers
|
||||||
|
_mqttClient.DisconnectedAsync += HandleMqttDisconnection;
|
||||||
|
_mqttClient.ApplicationMessageReceivedAsync += HandleMqttMessageReceived;
|
||||||
|
|
||||||
|
await ConnectMqttClient();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mqttSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConnectMqttClient()
|
||||||
|
{
|
||||||
|
if (_mqttClient == null || _isDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_mqttClient.IsConnected)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Connecting to MQTT broker at {Broker}:{Port}...", _mqttBroker, _mqttPort);
|
||||||
|
await _mqttClient.ConnectAsync(_mqttOptions, CancellationToken.None);
|
||||||
|
_logger.LogInformation("Connected to MQTT broker successfully");
|
||||||
|
|
||||||
|
await SubscribeToMqttTopics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to connect to MQTT broker");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleMqttDisconnection(MqttClientDisconnectedEventArgs args)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Disconnected from MQTT broker: {Reason}", args.Reason);
|
||||||
|
|
||||||
|
// ReasonCode 0 means normal disconnect, no need to log as error
|
||||||
|
if (args.Exception != null)
|
||||||
|
{
|
||||||
|
_logger.LogError(args.Exception, "MQTT disconnection error");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TryReconnectMqtt()
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await _mqttSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_mqttClient != null && !_mqttClient.IsConnected)
|
||||||
|
{
|
||||||
|
await ConnectMqttClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error during MQTT reconnection attempt");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mqttSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SubscribeToMqttTopics()
|
||||||
|
{
|
||||||
|
if (_mqttClient == null || !_mqttClient.IsConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Subscribing to MQTT topics...");
|
||||||
|
|
||||||
|
var topicFilter = new MqttTopicFilterBuilder()
|
||||||
|
.WithTopic("iot/devices/#")
|
||||||
|
.WithAtLeastOnceQoS()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var subscribeResult = await _mqttClient.SubscribeAsync(topicFilter);
|
||||||
|
|
||||||
|
foreach (var result in subscribeResult.Items)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Subscribed to topic '{Topic}' with ResultCode: {ResultCode}",
|
||||||
|
result.TopicFilter.Topic, result.ResultCode);
|
||||||
|
|
||||||
|
if (result.ResultCode != MqttClientSubscribeResultCode.GrantedQoS0 &&
|
||||||
|
result.ResultCode != MqttClientSubscribeResultCode.GrantedQoS1 &&
|
||||||
|
result.ResultCode != MqttClientSubscribeResultCode.GrantedQoS2)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Subscription to topic {Topic} was rejected: {Reason}",
|
||||||
|
result.TopicFilter.Topic, result.ResultCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error subscribing to MQTT topics");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleMqttMessageReceived(MqttApplicationMessageReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e == null || e.ApplicationMessage == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string topic = e.ApplicationMessage.Topic;
|
||||||
|
var payloadBytes = e.ApplicationMessage.Payload;
|
||||||
|
|
||||||
|
if (payloadBytes.IsEmpty)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Received empty payload for topic: {Topic}", topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string payloadJson = Encoding.UTF8.GetString(payloadBytes);
|
||||||
|
|
||||||
|
_logger.LogInformation("Received MQTT message: {Topic} -> {PayloadLength} bytes",
|
||||||
|
topic, payloadBytes.Length);
|
||||||
|
_logger.LogDebug("Message payload: {Payload}", payloadJson);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ProcessMqttMessage(topic, payloadJson);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error processing MQTT message for topic {Topic}", topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessMqttMessage(string topic, string payloadJson)
|
||||||
|
{
|
||||||
|
if (!topic.StartsWith("iot/devices/"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Handle device update message
|
||||||
|
if (topic.EndsWith("/update_PVT"))
|
||||||
|
{
|
||||||
|
await ProcessUpdatePvtMessage(topic, payloadJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more message handlers here as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessUpdatePvtMessage(string topic, string payloadJson)
|
||||||
|
{
|
||||||
|
DeviceMessage? messageData;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
messageData = JsonConvert.DeserializeObject<DeviceMessage>(payloadJson);
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to deserialize message payload");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageData == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("Deserialized message is null for topic {Topic}", topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
var deviceService = scope.ServiceProvider.GetRequiredService<IDeviceService>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool result = await deviceService.UpdatePVT(messageData);
|
||||||
|
string imei = messageData.Imei!;
|
||||||
|
string responseTopic = string.Format(MqttTopic.UpdatePVTResponseFormat, imei);
|
||||||
|
|
||||||
|
object responsePayload;
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Update PVT for device {Imei} succeeded", imei);
|
||||||
|
responsePayload = new
|
||||||
|
{
|
||||||
|
Imei = imei,
|
||||||
|
Status = "success",
|
||||||
|
Message = "Update PVT success",
|
||||||
|
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Update PVT for device {Imei} failed", imei);
|
||||||
|
responsePayload = new
|
||||||
|
{
|
||||||
|
Imei = imei,
|
||||||
|
Status = "error",
|
||||||
|
Message = "Update PVT failed",
|
||||||
|
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await PublishMqttMessage(responseTopic, responsePayload);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error processing PVT update for device {Imei}", messageData.Imei);
|
||||||
|
|
||||||
|
// Try to send error response
|
||||||
|
string responseTopic = string.Format(MqttTopic.UpdatePVTResponseFormat, messageData.Imei);
|
||||||
|
var errorPayload = new
|
||||||
|
{
|
||||||
|
Imei = messageData.Imei,
|
||||||
|
Status = "error",
|
||||||
|
Message = "Internal server error",
|
||||||
|
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||||
|
};
|
||||||
|
|
||||||
|
await PublishMqttMessage(responseTopic, errorPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PublishMqttMessage(string topic, object payload)
|
||||||
|
{
|
||||||
|
if (_mqttClient == null || !_mqttClient.IsConnected)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Cannot publish message: MQTT client is not connected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string payloadJson = JsonConvert.SerializeObject(payload);
|
||||||
|
|
||||||
|
var message = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic(topic)
|
||||||
|
.WithPayload(payloadJson)
|
||||||
|
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
|
.WithRetainFlag(false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await _mqttClient.PublishAsync(message, CancellationToken.None);
|
||||||
|
_logger.LogInformation("Published message to topic {Topic}", topic);
|
||||||
|
_logger.LogDebug("Message payload: {Payload}", payloadJson);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error publishing message to topic {Topic}", topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DisposeMqttClient()
|
||||||
|
{
|
||||||
|
await _mqttSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isDisposed = true;
|
||||||
|
|
||||||
|
if (_reconnectTimer != null)
|
||||||
|
{
|
||||||
|
_reconnectTimer.Stop();
|
||||||
|
_reconnectTimer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mqttClient != null)
|
||||||
|
{
|
||||||
|
if (_mqttClient.IsConnected)
|
||||||
|
{
|
||||||
|
await _mqttClient.DisconnectAsync();
|
||||||
|
_logger.LogInformation("Disconnected from MQTT broker");
|
||||||
|
}
|
||||||
|
|
||||||
|
_mqttClient.Dispose();
|
||||||
|
_mqttClient = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error disposing MQTT client");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mqttSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Stopping RuleEngineWorker");
|
||||||
|
await DisposeMqttClient();
|
||||||
|
await base.StopAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_mqttSemaphore.Dispose();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
404
RuleEngine/Services/DeviceService.cs
Normal file
404
RuleEngine/Services/DeviceService.cs
Normal file
|
@ -0,0 +1,404 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using RuleEngine.Common;
|
||||||
|
using RuleEngine.Constants;
|
||||||
|
using RuleEngine.Database;
|
||||||
|
using RuleEngine.DTOs;
|
||||||
|
using RuleEngine.Interfaces;
|
||||||
|
using RuleEngine.Models;
|
||||||
|
using RuleEngine.Requests;
|
||||||
|
|
||||||
|
namespace RuleEngine.Services
|
||||||
|
{
|
||||||
|
public class DeviceService : IDeviceService
|
||||||
|
{
|
||||||
|
private readonly DatabaseContext _dbContext;
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
private readonly static string logFolderPath = LogConstants.LOG_FILE_PATH;
|
||||||
|
private readonly static string exceptionFolder = LogConstants.LOG_EXCEPTION_FILE_PATH;
|
||||||
|
private static readonly Dictionary<string, LimitedSizeQueue<DateTime>> dictCarHistory = [];
|
||||||
|
|
||||||
|
public DeviceService(DatabaseContext dbContext, IConfiguration config)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
public async Task<bool> UpdatePVT(DeviceMessage deviceMessage)
|
||||||
|
{
|
||||||
|
DeviceLogRequest? deviceLogRequest = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var enableLogFile = _config.GetValue<bool>("EnableLogFile");
|
||||||
|
if (enableLogFile)
|
||||||
|
{
|
||||||
|
string Imei = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Imei = deviceMessage.Imei ?? string.Empty;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log the exception
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceLogRequest = CreatePVTRequest(deviceMessage);
|
||||||
|
if (deviceLogRequest == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (deviceLogRequest.Code == "ERROR")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (deviceLogRequest.GpsLat == 0 && deviceLogRequest.GpsLon == 0)
|
||||||
|
{
|
||||||
|
var online = _dbContext.Onlines.Where(x => (x.CarId ?? 0) == deviceLogRequest.CarID).FirstOrDefault();
|
||||||
|
if (online == null)
|
||||||
|
{
|
||||||
|
await UpdateOnlineProcedure(deviceLogRequest, deviceLogRequest.Imei);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((deviceLogRequest.GpsLat == 0) &&
|
||||||
|
(deviceLogRequest.GpsLon == 0))
|
||||||
|
{
|
||||||
|
deviceLogRequest.GpsLat = online.GpsLat;
|
||||||
|
deviceLogRequest.GpsLon = online.GpsLon;
|
||||||
|
}
|
||||||
|
//if ( (deviceLogRequest.NetworkLat == 0) &&
|
||||||
|
// (deviceLogRequest.NetworkLon == 0))
|
||||||
|
//{
|
||||||
|
// deviceLogRequest.NetworkLat = online.NetworkLat;
|
||||||
|
// deviceLogRequest.NetworkLon = online.NetworkLon;
|
||||||
|
//}
|
||||||
|
await UpdateOnlineProcedure(deviceLogRequest, deviceLogRequest.Imei);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await UpdateOnlineProcedure(deviceLogRequest, deviceLogRequest.Imei);
|
||||||
|
}
|
||||||
|
|
||||||
|
await InsertHistoryProcedure(deviceLogRequest, deviceLogRequest.Imei);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log the exception
|
||||||
|
if (Directory.Exists(logFolderPath))
|
||||||
|
StaticResources.Log2File(logFolderPath + "PVTException.txt", ex.ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceLogRequest? CreatePVTRequest(DeviceMessage deviceMessage, bool bReupPVT = false)
|
||||||
|
{
|
||||||
|
DeviceLogRequest? requestLog = null;
|
||||||
|
var strServerTime = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
|
||||||
|
string Imei = deviceMessage?.Imei ?? string.Empty;
|
||||||
|
string strReceivedTime = deviceMessage?.ReceivedTime ?? string.Empty;
|
||||||
|
DateTime receivedTime = DateTime.UtcNow;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
receivedTime = DateTime.ParseExact(strReceivedTime, "yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||||
|
if (!string.IsNullOrEmpty(strReceivedTime))
|
||||||
|
{
|
||||||
|
receivedTime = Convert.ToDateTime(strReceivedTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StaticResources.Log2File(logFolderPath + "PVTException.txt", ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((bReupPVT == true) && (Imei != null) && (Imei != ""))
|
||||||
|
{
|
||||||
|
if (!dictCarHistory.TryGetValue(Imei, out LimitedSizeQueue<DateTime>? queue))
|
||||||
|
{
|
||||||
|
queue = new LimitedSizeQueue<DateTime>(QueueConstant.QUEUESIZE);
|
||||||
|
dictCarHistory.Add(Imei, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue != null && !queue.Contains(receivedTime))
|
||||||
|
{
|
||||||
|
queue.Enqueue(receivedTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double longtitude = deviceMessage?.Longitude ?? 0;
|
||||||
|
double latitude = deviceMessage?.Latitude ?? 0;
|
||||||
|
int gpsSpeed = deviceMessage?.GpsSpeed ?? 0;
|
||||||
|
bool isSos = deviceMessage?.IsSOS ?? false;
|
||||||
|
bool isStrongBoxOpen = deviceMessage?.IsStrongBoxOpen ?? false; //co mo ket: dong/mo
|
||||||
|
bool isEngineOn = deviceMessage?.IsEngineOn ?? false; //co dong co bat/tat
|
||||||
|
bool isStopping = deviceMessage?.IsStopping ?? false; //co dung do dung/do
|
||||||
|
bool isGPSLost = deviceMessage?.IsGPSLost ?? true; //co GPS mat/co
|
||||||
|
int totalImageCam1 = deviceMessage?.TotalImgCam1 ?? 0;
|
||||||
|
int totalImageCam2 = deviceMessage?.TotalImgCam2 ?? 0;
|
||||||
|
string strRFID = deviceMessage?.StrRFID ?? string.Empty;
|
||||||
|
string strOBD = deviceMessage?.StrOBD ?? string.Empty;
|
||||||
|
string strLac = deviceMessage?.LacID ?? string.Empty;
|
||||||
|
string version = deviceMessage?.Version ?? string.Empty;
|
||||||
|
var gpsInfor = deviceMessage?.GpsInfor;
|
||||||
|
bool isJamming = deviceMessage?.IsJamming ?? false;
|
||||||
|
bool isSpoofing = deviceMessage?.IsSpoofing ?? false;
|
||||||
|
bool cam1OK = deviceMessage?.Cam1OK ?? false;
|
||||||
|
bool cam2OK = deviceMessage?.Cam2OK ?? false;
|
||||||
|
bool camDetach = deviceMessage?.CamDetach ?? false;
|
||||||
|
double cell_lat = 0;
|
||||||
|
double cell_lon = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (gpsInfor != null)
|
||||||
|
{
|
||||||
|
int cellid = gpsInfor.Cellid != null ? Convert.ToInt32(gpsInfor.Cellid) : 0;
|
||||||
|
int lacid = gpsInfor.Lacid != null ? Convert.ToInt32(gpsInfor.Lacid) : 0;
|
||||||
|
int mcc = gpsInfor.Mcc != null ? Convert.ToInt32(gpsInfor.Mcc) : 0;
|
||||||
|
int mnc = gpsInfor.Mnc != null ? Convert.ToInt32(gpsInfor.Mnc) : 0;
|
||||||
|
string strCellType = gpsInfor.NetworkType != null ? gpsInfor.NetworkType.Trim() : string.Empty;
|
||||||
|
if ((cellid > 0) && (strCellType != null))
|
||||||
|
{
|
||||||
|
strCellType = strCellType.ToLower();
|
||||||
|
int? CellTypeGoogle = 0;
|
||||||
|
if (strCellType.Contains("cellsignalstrengthgsm"))
|
||||||
|
CellTypeGoogle = 3;
|
||||||
|
else if (strCellType.Contains("cellsignalstrengthcdma"))
|
||||||
|
CellTypeGoogle = 2;
|
||||||
|
else if (strCellType.Contains("cellsignalstrengthwcdma"))
|
||||||
|
CellTypeGoogle = 1;
|
||||||
|
else if (strCellType.Contains("cellsignalstrengthlte"))
|
||||||
|
CellTypeGoogle = 0;
|
||||||
|
var cellinfor = _dbContext.CellInfors.Where(x => (x.LacId == lacid) &&
|
||||||
|
(x.CellId == cellid) &&
|
||||||
|
(x.MCC == mcc) && (x.MNC == mnc)
|
||||||
|
&& (x.NetworkType == CellTypeGoogle)
|
||||||
|
).FirstOrDefault();
|
||||||
|
|
||||||
|
if (cellinfor == null)
|
||||||
|
{
|
||||||
|
if (CellTypeGoogle != null)
|
||||||
|
{
|
||||||
|
cellinfor = new CellInfor
|
||||||
|
{
|
||||||
|
CellId = cellid,
|
||||||
|
LacId = lacid,
|
||||||
|
MCC = mcc,
|
||||||
|
MNC = mnc,
|
||||||
|
NetworkType = CellTypeGoogle
|
||||||
|
};
|
||||||
|
_dbContext.CellInfors.Add(cellinfor);
|
||||||
|
_dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell_lat = cellinfor.NetworkLat ?? 0;
|
||||||
|
cell_lon = cellinfor.NetworkLon ?? 0;
|
||||||
|
if (cell_lat != 0 && cell_lon != 0 && latitude != 0 && longtitude != 0)
|
||||||
|
{
|
||||||
|
var dist = StaticResources.DistanceGpsCalculate(latitude, longtitude, cell_lat, cell_lon, 'K');
|
||||||
|
if (dist > DeviceConfig.SPOOFING_THRESH_DISTANCE)
|
||||||
|
{
|
||||||
|
isSpoofing = true;
|
||||||
|
var strLog = string.Format($"{strServerTime} {Imei} {cellinfor.Id} {cellinfor.NetworkLat} {cellinfor.NetworkLon} {latitude} {longtitude} {dist}");
|
||||||
|
if (Directory.Exists(logFolderPath))
|
||||||
|
StaticResources.Log2File(logFolderPath + "SpoofingDetection.txt", strLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(logFolderPath))
|
||||||
|
StaticResources.Log2File(logFolderPath + "CELLException.txt", ex.ToString());
|
||||||
|
}
|
||||||
|
return requestLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateOnlineProcedure(DeviceLogRequest request, string imei)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime currentTimeUTC = DateTime.UtcNow;
|
||||||
|
DateTime localTime = currentTimeUTC.AddHours(7);
|
||||||
|
|
||||||
|
if (request.ReceivedTime.Year <= (localTime.Year - 1)) //lost GPS
|
||||||
|
{
|
||||||
|
await UpdateLostGpsOnline(request.CarID, true, localTime);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// loc ban tin tuong lai
|
||||||
|
TimeSpan diff02 = localTime.AddMinutes(5).Subtract(request.ReceivedTime);
|
||||||
|
if (diff02.TotalSeconds > 0) // TotalSeconds < 0 - ban tin gui lai, ko cap nhat bang truc tuyen
|
||||||
|
{
|
||||||
|
await UpdateOnlines(request, localTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StaticResources.LogException2File(exceptionFolder, imei + "_ex_pvt", DateTime.UtcNow.ToString() + " InsertLst_TrucTuyen " + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateOnlines(DeviceLogRequest request, DateTime localTime)
|
||||||
|
{
|
||||||
|
var isSos = request.IsSos == true;
|
||||||
|
var isGpsLost = request.IsGPSLost == true;
|
||||||
|
var strongBoxOpen = request.IsStrongBoxOpen == true;
|
||||||
|
var engineOn = request.IsEngineOn == true;
|
||||||
|
|
||||||
|
var carId = request.CarID;
|
||||||
|
var deviceTime = request.ReceivedTime;
|
||||||
|
var resetStatusMinute = DeviceConfig.SECONDS_SET_CAR_STOP / 60;
|
||||||
|
|
||||||
|
var existingOnline = await _dbContext.Onlines
|
||||||
|
.FindAsync(carId);
|
||||||
|
|
||||||
|
if (existingOnline != null)
|
||||||
|
{
|
||||||
|
var diffSeconds = (deviceTime - existingOnline.DeviceTime)?.TotalSeconds ?? 0;
|
||||||
|
|
||||||
|
if (diffSeconds <= 0)
|
||||||
|
{
|
||||||
|
// Device sent an older or duplicate message; do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int carStatus = request.CarStatus ?? 0;
|
||||||
|
if (diffSeconds > resetStatusMinute)
|
||||||
|
{
|
||||||
|
carStatus = 2; // timeout -> set as disconnected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update record
|
||||||
|
existingOnline.GpsVelocity = request.GpsSpeed ?? 0;
|
||||||
|
existingOnline.IsSos = isSos;
|
||||||
|
existingOnline.IsGPSLost = isGpsLost;
|
||||||
|
existingOnline.StrongBoxOpen = strongBoxOpen;
|
||||||
|
existingOnline.EngineOn = engineOn;
|
||||||
|
existingOnline.CarStatus = carStatus;
|
||||||
|
existingOnline.Rfidstring = request.StrRFID;
|
||||||
|
existingOnline.CellId = request.CellID;
|
||||||
|
existingOnline.LacId = request.LacID ?? 0;
|
||||||
|
existingOnline.GpsLat = request.GpsLat ?? 0;
|
||||||
|
existingOnline.GpsLon = request.GpsLon ?? 0;
|
||||||
|
existingOnline.NetworkLat = request.NetworkLat ?? 0;
|
||||||
|
existingOnline.NetworkLon = request.NetworkLon ?? 0;
|
||||||
|
existingOnline.IsJamming = request.IsJamming == true;
|
||||||
|
existingOnline.IsSpoofing = request.IsSpoofing == true;
|
||||||
|
existingOnline.IsCam1OK = request.Cam1OK == true;
|
||||||
|
existingOnline.IsCam2OK = request.Cam2OK == true;
|
||||||
|
existingOnline.GpsInfor = request.GPSInfor ?? "";
|
||||||
|
existingOnline.ReceivedTime = localTime;
|
||||||
|
existingOnline.DeviceTime = deviceTime;
|
||||||
|
existingOnline.AppVersion = request.Version;
|
||||||
|
|
||||||
|
_dbContext.Onlines.Update(existingOnline);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Insert new record
|
||||||
|
var newOnline = new Online
|
||||||
|
{
|
||||||
|
CarId = carId,
|
||||||
|
GpsVelocity = request.GpsSpeed ?? 0,
|
||||||
|
IsSos = isSos,
|
||||||
|
IsGPSLost = isGpsLost,
|
||||||
|
StrongBoxOpen = strongBoxOpen,
|
||||||
|
EngineOn = engineOn,
|
||||||
|
CarStatus = request.CarStatus ?? 0,
|
||||||
|
Rfidstring = request.StrRFID,
|
||||||
|
CellId = request.CellID,
|
||||||
|
LacId = request.LacID ?? 0,
|
||||||
|
GpsLat = request.GpsLat ?? 0,
|
||||||
|
GpsLon = request.GpsLon ?? 0,
|
||||||
|
NetworkLat = request.NetworkLat ?? 0,
|
||||||
|
NetworkLon = request.NetworkLon ?? 0,
|
||||||
|
IsJamming = request.IsJamming == true,
|
||||||
|
IsSpoofing = request.IsSpoofing == true,
|
||||||
|
IsCam1OK = request.Cam1OK == true,
|
||||||
|
IsCam2OK = request.Cam2OK == true,
|
||||||
|
GpsInfor = request.GPSInfor ?? "",
|
||||||
|
ReceivedTime = localTime,
|
||||||
|
DeviceTime = deviceTime,
|
||||||
|
AppVersion = request.Version
|
||||||
|
};
|
||||||
|
|
||||||
|
await _dbContext.Onlines.AddAsync(newOnline);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InsertHistoryProcedure(DeviceLogRequest request, string imei)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime currentTimeUTC = DateTime.UtcNow;
|
||||||
|
DateTime localTime = currentTimeUTC.AddHours(7);
|
||||||
|
|
||||||
|
var history = new History
|
||||||
|
{
|
||||||
|
CarId = request.CarID,
|
||||||
|
UnitId = request.UnitID,
|
||||||
|
GpsVelocity = request.GpsSpeed ?? 0,
|
||||||
|
IsSos = request.IsSos == true,
|
||||||
|
IsGPSLost = request.IsGPSLost == true,
|
||||||
|
StrongBoxOpen = request.IsStrongBoxOpen == true,
|
||||||
|
EngineOn = request.IsEngineOn == true,
|
||||||
|
CarStatus = request.CarStatus ?? 0,
|
||||||
|
Rfidstring = request.StrRFID,
|
||||||
|
GpsLat = request.GpsLat ?? 0,
|
||||||
|
GpsLon = request.GpsLon ?? 0,
|
||||||
|
GpsInfor = request.GPSInfor ?? string.Empty,
|
||||||
|
IsJamming = request.IsJamming == true,
|
||||||
|
IsSpoofing = request.IsSpoofing == true,
|
||||||
|
Cam1OK = request.Cam1OK == true,
|
||||||
|
Cam2OK = request.Cam2OK == true,
|
||||||
|
CamDetach = request.CamDetach == true,
|
||||||
|
ReceivedTime = localTime,
|
||||||
|
DeviceTime = request.ReceivedTime,
|
||||||
|
DeviceDate = int.Parse(request.ReceivedTime.ToString("yyyyMMdd"))
|
||||||
|
};
|
||||||
|
|
||||||
|
await _dbContext.Histories.AddAsync(history);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StaticResources.LogException2File(exceptionFolder, imei + "_ex_pvt", DateTime.UtcNow.ToString() + " InsertHistoryProcedure " + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateLostGpsOnline(int carId, bool isGpsLost, DateTime receivedTime)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var online = await _dbContext.Onlines.FindAsync(carId);
|
||||||
|
|
||||||
|
if (online != null)
|
||||||
|
{
|
||||||
|
online.IsGPSLost = isGpsLost;
|
||||||
|
online.ReceivedTime = receivedTime;
|
||||||
|
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StaticResources.LogException2File(exceptionFolder, carId + "_ex_pvt", DateTime.UtcNow.ToString() + " UpdateLostGpsOnline " + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
8
RuleEngine/appsettings.Development.json
Normal file
8
RuleEngine/appsettings.Development.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
RuleEngine/appsettings.json
Normal file
55
RuleEngine/appsettings.json
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"Using": [
|
||||||
|
"Serilog.Sinks.File"
|
||||||
|
],
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"WriteTo": [
|
||||||
|
{
|
||||||
|
"Name": "File",
|
||||||
|
"Args": {
|
||||||
|
"path": "Logs\/ruleengine_log_.log",
|
||||||
|
"rollingInterval": "Day",
|
||||||
|
"fileSizeLimitBytes": 10485760,
|
||||||
|
"rollOnFileSizeLimit": true,
|
||||||
|
"retainedFileCountLimit": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MqttSettings": {
|
||||||
|
"Broker": "localhost",
|
||||||
|
"Port": 1883,
|
||||||
|
"JwtSecret": "emqx@sivan123456789@emqx@sivan123456789@emqx@sivan123456789@emqx@sivan123456789",
|
||||||
|
"Username": "emqxruleengine",
|
||||||
|
"Password": "navis@123",
|
||||||
|
"Topic": {
|
||||||
|
"DeviceStatus": "devices/status",
|
||||||
|
"DeviceHealth": "devices/health",
|
||||||
|
"PushCommand": "devices/command"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RuleEngineConnectStr": "Data Source=203.171.20.94,1434;Database=LPB.CarTracking;User ID=mbbcartracking05;Password=navis@123"
|
||||||
|
},
|
||||||
|
"EnableLogFile": true,
|
||||||
|
"LogFolderPath": "..\/Logs",
|
||||||
|
"DefaultServerUrl": "",
|
||||||
|
"SPOOFING_THRESHOLD_KM": 2000,
|
||||||
|
"STOP_SPEED_LIMIT": 20,
|
||||||
|
"STOP_POINT_DISTANCE": 0.2,
|
||||||
|
"DeviceLogLength": 20000000,
|
||||||
|
"DevicePasswordSalt": "navis@salt"
|
||||||
|
}
|
51
SimulatedTrackingDevice/DTOs/DeviceMessage.cs
Normal file
51
SimulatedTrackingDevice/DTOs/DeviceMessage.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
namespace SimulatedTrackingDevice.DTOs
|
||||||
|
{
|
||||||
|
public class DeviceMessage
|
||||||
|
{
|
||||||
|
public string? Imei { get; set; }
|
||||||
|
public string? ReceivedTime { get; set; }
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
public int GpsSpeed { get; set; }
|
||||||
|
public string? CelID { get; set; }
|
||||||
|
public string? LacID { get; set; }
|
||||||
|
public bool IsSOS { get; set; }
|
||||||
|
public bool IsStrongBoxOpen { get; set; }
|
||||||
|
public bool IsEngineOn { get; set; }
|
||||||
|
public bool IsStopping { get; set; }
|
||||||
|
public bool IsGPSLost { get; set; }
|
||||||
|
public int TotalImgCam1 { get; set; }
|
||||||
|
public int TotalImgCam2 { get; set; }
|
||||||
|
public string? StrRFID { get; set; }
|
||||||
|
public string? StrOBD { get; set; }
|
||||||
|
public string? Provider { get; set; }
|
||||||
|
public string? Version { get; set; }
|
||||||
|
public int CpuTime { get; set; }
|
||||||
|
public int GeoMinDistance { get; set; }
|
||||||
|
public double NearestLat { get; set; }
|
||||||
|
public double NearestLon { get; set; }
|
||||||
|
public int NearestID { get; set; }
|
||||||
|
public string? NearestName { get; set; }
|
||||||
|
public int Maxsnr { get; set; }
|
||||||
|
public string? CpuTemp { get; set; }
|
||||||
|
public GpsInfor? GpsInfor { get; set; }
|
||||||
|
public bool Cam1OK { get; set; }
|
||||||
|
public bool Cam2OK { get; set; }
|
||||||
|
public bool CamDetach { get; set; }
|
||||||
|
public bool IsJamming { get; set; }
|
||||||
|
public bool IsSpoofing { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GpsInfor
|
||||||
|
{
|
||||||
|
public double AgcLevelDb { get; set; }
|
||||||
|
public double Maxsnr { get; set; }
|
||||||
|
public int SatelliteUseInFixed { get; set; }
|
||||||
|
public string? Cellid { get; set; }
|
||||||
|
public string? Lacid { get; set; }
|
||||||
|
public string? Mcc { get; set; }
|
||||||
|
public string? Mnc { get; set; }
|
||||||
|
public string? NetworkType { get; set; }
|
||||||
|
public string? SignalStrength { get; set; }
|
||||||
|
}
|
||||||
|
}
|
167
SimulatedTrackingDevice/Program.cs
Normal file
167
SimulatedTrackingDevice/Program.cs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text;
|
||||||
|
using MQTTnet;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace SimulatedTrackingDevice
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
private static readonly string BrokerIp = "127.0.0.1";
|
||||||
|
//private static readonly string BrokerIp = "localhost";
|
||||||
|
private static readonly int BrokerPort = 1883;
|
||||||
|
private static readonly string Username = "emqxdevice";
|
||||||
|
private static readonly string Password = "sivan@123";
|
||||||
|
private static readonly string DevicePasswordSalt = "navis@salt";
|
||||||
|
private static readonly int secondsToReconnect = 2000;
|
||||||
|
private static readonly int secondsToHealthcheck = 10000;
|
||||||
|
|
||||||
|
//private static readonly int DeviceCount = 1000;
|
||||||
|
|
||||||
|
private static readonly string UpdatePVTTopic = "iot/device/update_pvt";
|
||||||
|
private static readonly ConcurrentDictionary<string, string> deviceTokens = new();
|
||||||
|
private static readonly ConcurrentDictionary<string, SemaphoreSlim> deviceSemaphores = new();
|
||||||
|
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
List<Task> deviceTasks = new();
|
||||||
|
for (int i = 1; i <= 1; i++)
|
||||||
|
{
|
||||||
|
string localMacAddress = $"DeviceTest{i:0000}";
|
||||||
|
deviceSemaphores[localMacAddress] = new SemaphoreSlim(1, 1); // Tạo semaphore riêng cho từng thiết bị
|
||||||
|
//deviceTasks.Add(AllDevicePublish(macAddress, i));
|
||||||
|
deviceTasks.Add(SimulateDevice(localMacAddress));
|
||||||
|
|
||||||
|
await Task.Delay(50); // Tránh quá tải khi tạo quá nhiều thiết bị cùng lúc
|
||||||
|
}
|
||||||
|
await Task.WhenAll(deviceTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task SimulateDevice(string localMacAddress)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var factory = new MqttClientFactory();
|
||||||
|
var mqttClient = factory.CreateMqttClient();
|
||||||
|
string token = "";
|
||||||
|
|
||||||
|
var options = new MqttClientOptionsBuilder()
|
||||||
|
.WithClientId(localMacAddress)
|
||||||
|
.WithTcpServer(BrokerIp, BrokerPort)
|
||||||
|
.WithCredentials(Username, Password)
|
||||||
|
.WithCleanSession()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
mqttClient.ApplicationMessageReceivedAsync += async e =>
|
||||||
|
{
|
||||||
|
var macAddressProperty = e.ApplicationMessage.UserProperties
|
||||||
|
.FirstOrDefault(p => p.Name == "MacAddress")?.Value;
|
||||||
|
if (macAddressProperty == localMacAddress)
|
||||||
|
{
|
||||||
|
var commandTopic = "iot/server/" + localMacAddress + "/command";
|
||||||
|
string topic = e.ApplicationMessage.Topic;
|
||||||
|
string payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
|
||||||
|
|
||||||
|
if (topic == ResponseTopic)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(payload))
|
||||||
|
{
|
||||||
|
var response = System.Text.Json.JsonSerializer.Deserialize<DeviceRegisterResponse>(payload);
|
||||||
|
token = response.AccessToken;
|
||||||
|
|
||||||
|
// Unsubscribe khỏi topic response sau khi nhận token
|
||||||
|
await mqttClient.UnsubscribeAsync(ResponseTopic);
|
||||||
|
Console.WriteLine(localMacAddress + ": Unsubscribed from response topic");
|
||||||
|
|
||||||
|
// log file
|
||||||
|
Log2File("./LogFiles/" + localMacAddress + ".txt", DateTime.UtcNow.AddHours(7).ToString("yyyy/MM/dd HH:mm:ss") + " - Received: " + payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (topic == commandTopic)
|
||||||
|
{
|
||||||
|
// xu ly lenh gui tu server
|
||||||
|
Console.WriteLine(localMacAddress + ": " + payload);
|
||||||
|
// log file
|
||||||
|
Log2File("./LogFiles/" + localMacAddress + ".txt", DateTime.UtcNow.AddHours(7).ToString("yyyy/MM/dd HH:mm:ss") + " - Received: " + payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(options, CancellationToken.None);
|
||||||
|
await mqttClient.SubscribeAsync(ResponseTopic, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
await SendRegistrationRequest(mqttClient, localMacAddress);
|
||||||
|
|
||||||
|
while (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var _factory = new MqttClientFactory();
|
||||||
|
var _mqttClient = _factory.CreateMqttClient();
|
||||||
|
var _options = new MqttClientOptionsBuilder()
|
||||||
|
.WithClientId(localMacAddress)
|
||||||
|
.WithTcpServer(BrokerIp, BrokerPort)
|
||||||
|
.WithCredentials(localMacAddress, token)
|
||||||
|
.WithCleanSession()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_mqttClient.DisconnectedAsync += async (e) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"MQTT Disconnected. Reason: {e.ToString()}");
|
||||||
|
|
||||||
|
// Nếu lỗi có thể do token hết hạn, gửi lại request đăng ký
|
||||||
|
if (e.Reason == MqttClientDisconnectReason.NotAuthorized)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Token might be expired. Sending Register...");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await _mqttClient.ConnectAsync(_options, CancellationToken.None);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
await SendHealthData(_mqttClient, localMacAddress, token);
|
||||||
|
await Task.Delay(10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// log file
|
||||||
|
Log2File("./LogFiles/Exceptions.txt", DateTime.UtcNow.AddHours(7).ToString("yyyy/MM/dd HH:mm:ss") + "- User: " + Username + " - " + localMacAddress + ": " + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Log2File(string filePath, string data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo info = new FileInfo(filePath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (info.Exists && info.Length > 10000000) // delete the file first if 10 MB
|
||||||
|
{
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
using (var fs = File.Open(filePath, FileMode.Append))
|
||||||
|
{
|
||||||
|
using (var sw = new StreamWriter(fs))
|
||||||
|
{
|
||||||
|
sw.WriteLine(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
SimulatedTrackingDevice/SimulatedTrackingDevice.csproj
Normal file
15
SimulatedTrackingDevice/SimulatedTrackingDevice.csproj
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
Reference in New Issue
Block a user