using Edge.Core.Processor;
using Edge.Core.IndustryStandardInterface.Pump;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Edge.Core.IndustryStandardInterface.ATG;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Edge.Core.Database;
using AutoMapper;
using Edge.Core.UniversalApi;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using System.Text.RegularExpressions;
using Application.ATG_Classic_App.Model;
using System.Diagnostics.CodeAnalysis;
using System.Timers;
using System.Threading;
using Edge.Core.Processor.Dispatcher.Attributes;
namespace Application.ATG_Classic_App
{
[UniversalApi(Name = "OnStateChange", EventDataType = typeof(AtgStateChangeEventArg), Description = "will fire on Atg state changed")]
[UniversalApi(Name = "OnAlarm", EventDataType = typeof(AtgAlarmEventArg), Description = "will fire on Atg alarms detected")]
[MetaPartsDescriptor(
"lang-zh-cn:Pro-gauge 液位仪lang-en-us:Pro-gauge ATG",
"lang-zh-cn:用于总控所有连入此系统的 Pro-gauge 探棒,并提供液位仪控制台功能lang-en-us:Used for overall control all Pro-gauge probes connected in the system, and providing functions of the ATG console",
new[] { "lang-zh-cn:液位仪lang-en-us:ATG" })]
public class App : IAppProcessor, IAutoTankGaugeController
{
private ILogger logger = NullLogger.Instance;
private System.Timers.Timer polling_fast_TankReadingTimer;
///
/// DO NOT lower down this value unless you understand the consequence.
///
public int polling_fast_TankReadingTimer_Internval = 500;
private int polling_fast_TankReadingTimer_Buffer_MaxLength_By_Second = 60 * 60 * 3;
///
/// for track last Inventory saved to db time, and compare to current polling fast timer, if inventory sampling
/// interval time reached, then start a new db saving.
///
private DateTime lastInventoriesSavedIntoDatabaseTime = DateTime.MinValue;
private IServiceProvider services;
private IEnumerable probeHandlers;
private IEnumerable richTankInfos = null;
public string MetaConfigName { get; set; }
public int DeviceId { get; }
public IEnumerable Tanks => this.richTankInfos.Select(c => c.Tank);
public SystemUnit SystemUnit { get; }
public event EventHandler OnStateChange;
public event EventHandler OnAlarm;
#region UniversalApi - Config
[UniversalApi(Description = "Get the TankOverallConfig, and all TankConfigs.")]
public async Task>> GetConfigAsync()
{
var dbContext = this.DbContextFactory.CreateDbContext();
var tankOverallConfig = await dbContext.TankOverallConfigs
.OrderByDescending(c => c.ModifiedTimeStamp)
.ThenByDescending(c => c.CreatedTimeStamp).FirstOrDefaultAsync();
var haveToDoThisToMakeInMemoryDbUsedInUnitTestToWork = await dbContext.TankConfigs
.Include(tc => tc.TankLimitConfig)
.Include(tc => tc.ProbeConfig)
.Include(tc => tc.ProductConfig)
.Include(tc => tc.TankProfileDatas).ToListAsync();
var tankConfigs = haveToDoThisToMakeInMemoryDbUsedInUnitTestToWork.GroupBy(tc => tc.TankNumber)
.Select(tcGp => tcGp.OrderByDescending(c => c.ModifiedTimeStamp)
.ThenByDescending(c => c.CreatedTimeStamp).FirstOrDefault());
//var tankConfigs = await dbContext.TankConfigs
// .Include(tc => tc.TankLimitConfig)
// .Include(tc => tc.ProbeConfig)
// .Include(tc => tc.ProductConfig)
// .GroupBy(tc => tc.TankNumber)
// .Select(tcGp => tcGp.OrderByDescending(c => c.ModifiedTimeStamp)
// .ThenByDescending(c => c.CreatedTimeStamp).FirstOrDefault()).ToListAsync();
return new Tuple>(tankOverallConfig, tankConfigs);
}
[UniversalApi(Description = "Udpate or insert the config, the input and sample could be either of-> " +
"TankOverallConfig: " + "{\"TcReference\":25,\"InventorySamplingInterval\":5000,\"DeliveryMode\":0,\"Id\":0}, " +
"TankConfig: " + "{\"TankNumber\":1,\"Label\":\"I\u0027m Tank with Number 1\",\"Diameter\":1000,\"ThermalCoefficient\":0,\"DeliveryDelay\":0,\"TankProfileDatas\":null,\"ProductConfigId\":null,\"ProductConfig\":{\"ProductCode\":\"91\",\"ProductLabel\":\"91#\",\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null},\"TankLimitConfigId\":null,\"TankLimitConfig\":{\"MaxVolume\":1100,\"FullVolume\":1200,\"HighProduct\":900,\"LowProduct\":100,\"HighWaterWarning\":10,\"HighWaterAlarm\":40,\"FuelTemperatureLowLimit\":1,\"FuelTemperatureHighLimit\":2,\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null},\"ProbeConfigId\":null,\"ProbeConfig\":{\"DeviceId\":1,\"ProbeOffset\":0,\"WaterOffset\":0,\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null},\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null}" + ", " +
"TankLimitConfig: " + "{\"MaxVolume\":4400,\"FullVolume\":4800,\"HighProduct\":3600,\"LowProduct\":400,\"HighWaterWarning\":40,\"HighWaterAlarm\":160,\"FuelTemperatureLowLimit\":4,\"FuelTemperatureHighLimit\":8,\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null}" + ", " +
"ProbeConfig: " + "{\"DeviceId\":4,\"ProbeOffset\":0,\"WaterOffset\":0,\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null}" + ", " +
"ProductConfig: " + "{\"ProductCode\":\"94\",\"ProductLabel\":\"94#\",\"Id\":0,\"CreatedTimeStamp\":\"0001-01-01T00:00:00\",\"ModifiedTimeStamp\":null}")]
public async Task UpsertConfigAsync(BaseConfig inputConfig)
{
/*update or insert a db row*/
if (inputConfig == null) throw new ArgumentException(nameof(inputConfig));
//BaseConfig inputConfig = null;
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
//switch (input.Parameters.First().Name)
//{
// case "TankOverallConfig":
// {
// inputConfig = JsonSerializer.Deserialize(input.Parameters.First().Value, options);
// break;
// }
// case "TankConfig":
// {
// inputConfig = JsonSerializer.Deserialize(input.Parameters.First().Value, options);
// break;
// }
// case "TankLimitConfig":
// {
// inputConfig = JsonSerializer.Deserialize(input.Parameters.First().Value, options);
// break;
// }
// case "ProbeConfig":
// {
// inputConfig = JsonSerializer.Deserialize(input.Parameters.First().Value, options);
// break;
// }
// case "ProductConfig":
// {
// inputConfig = JsonSerializer.Deserialize(input.Parameters.First().Value, options);
// break;
// }
// default:
// throw new InvalidOperationException("Unknown Config name: " + (input.Parameters.First().Name ?? ""));
//}
var dbContext = this.DbContextFactory.CreateDbContext();// new SqliteDbContext();
if (inputConfig.Id == 0)
{
/*create one in db*/
inputConfig.CreatedTimeStamp = DateTime.Now;
inputConfig.ModifiedTimeStamp = null;
dbContext.Add(inputConfig);
var effectedRowCount = await dbContext.SaveChangesAsync();
}
else
{
/*udpate one in db*/
inputConfig.ModifiedTimeStamp = DateTime.Now;
dbContext.Entry(inputConfig).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
dbContext.Entry(inputConfig).Property(c => c.CreatedTimeStamp).IsModified = false;
//dbContext.Entry(inputTankOverallConfig).Property(c => c.ModifiedTimeStamp).IsModified = true;
await dbContext.SaveChangesAsync();
}
var haveToDoThisToMakeInMemoryDbUsedInUnitTestToWork = await dbContext.TankConfigs
.Include(tc => tc.TankLimitConfig)
.Include(tc => tc.ProbeConfig)
.Include(tc => tc.ProductConfig)
.Include(tc => tc.TankProfileDatas).ToListAsync();
var tankConfigs = haveToDoThisToMakeInMemoryDbUsedInUnitTestToWork.GroupBy(tc => tc.TankNumber)
.Select(tcGp => tcGp.OrderByDescending(c => c.ModifiedTimeStamp)
.ThenByDescending(c => c.CreatedTimeStamp).FirstOrDefault());
var tankOverallConfig = await dbContext.Set()//.TankOverallConfigs
.OrderByDescending(c => c.ModifiedTimeStamp)
.ThenByDescending(c => c.CreatedTimeStamp).FirstOrDefaultAsync();
this.richTankInfos = this.TryReCreateRichTankInfos(tankConfigs, tankOverallConfig, this.probeHandlers);
if (this.richTankInfos == null)
{
this.State = AtgState.Inoperative_MissingConfig;
var onStateChangeEventArg = new AtgStateChangeEventArg(this.State,
"Necessary tank configs were missed or invalid after the UpsertConfigAsync(...), fix them and wait for notify.");
this.OnStateChange?.Invoke(this, onStateChangeEventArg);
var universalApiHub = this.services.GetRequiredService();
await universalApiHub.FireEvent(this, "OnStateChange", onStateChangeEventArg);
return inputConfig;
}
else
{
this.State = AtgState.Idle;
var onStateChangeEventArg = new AtgStateChangeEventArg(AtgState.TanksReloaded, "All tanks reloaded due to Config updated");
this.OnStateChange?.Invoke(this, onStateChangeEventArg);
var universalApiHub = this.services.GetRequiredService();
await universalApiHub.FireEvent(this, "OnStateChange", onStateChangeEventArg);
return inputConfig;
}
}
[UniversalApi(Description = "add a profile data for a tank.", InputParametersExampleJson = "[1,\"Height0:Volume0,Height1:Volume1,Height2:Volume2...\"]")]
public async Task