using Edge.Core.Database; using Edge.Core.IndustryStandardInterface.Pump; using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump; using SinoChemFC2PosProxy; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Threading; using System.Threading.Tasks; using Wayne.FDCPOSLibrary; using Edge.Core.Configuration; namespace SinochemInternetPlusApp { /// /// Manages pump transactions, inserting the transactions to POS database (table: Xiaofei2). /// public class ForecourtTrxManager { #region Logger static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Application"); #endregion #region Fields private IEnumerable nozzleProductConfig = Configurator.Default.NozzleExtraInfoConfiguration.Mapping; /// /// fdc pump handler is the entry for manage pumps. /// private IEnumerable fdcPumpControllers; /// /// PumpIdAsSideA:PumpIdAsSideB:JiHao /// private readonly IEnumerable> pumpSidePairs = null; /// /// pumpId:RealHoseLogicalId:HuiTianSiteLevelHoseId /// public static IEnumerable> nozzleRemappingPairs = null; /// /// rawName:FriendlyName /// private Dictionary rawProductNameToPosProductNameMapping = new Dictionary(); private string sqlServerConnStr; /// /// mapping between PumpId, nozzleId, site level nozzle... /// private IOrderedEnumerable sortedSiteNozzlesDataRows; private bool initialized = false; #endregion #region Constructor /// /// /// /// PumpIdAsSideA:PumpIdAsSideB:JiHao, like 1:2:1; 3:4:2; 5:6:3; /// pumpId:RealHoseLogicalId:HuiTianSiteLevelHoseId, like 14:2:16;14:1:17 /// rawGradeName:HuiTianPosGradeName, like 93gas:93#;98gas:98#; public ForecourtTrxManager(IEnumerable pumpControllers, string pumpSideMappingStr, string forceMappingFusionHoseToHuiTianHoseStr, string sqlServerConnStr, string rawProductNameToPosProductNameStr) { fdcPumpControllers = pumpControllers; var pumpSideMappingRawString = pumpSideMappingStr.Trim().Replace(" ", ""); if (pumpSideMappingRawString.Substring(pumpSideMappingRawString.Length - 1) == ";") pumpSideMappingRawString = pumpSideMappingRawString.Substring(0, pumpSideMappingRawString.Length - 1); pumpSidePairs = pumpSideMappingRawString.Split(';') .Select(p => new Tuple(int.Parse(p.Split(':')[0]), int.Parse(p.Split(':')[1]), int.Parse(p.Split(':')[2]))); if (!string.IsNullOrEmpty(forceMappingFusionHoseToHuiTianHoseStr)) { var nozzleRemappingRawString = forceMappingFusionHoseToHuiTianHoseStr.Trim().Replace(" ", ""); if (nozzleRemappingRawString.Substring(nozzleRemappingRawString.Length - 1) == ";") nozzleRemappingRawString = nozzleRemappingRawString.Substring(0, nozzleRemappingRawString.Length - 1); logger.Info("will parse forceMappingFusionHoseToHuiTianHose, raw string in config is: " + nozzleRemappingRawString); nozzleRemappingPairs = nozzleRemappingRawString.Split(';').Select(p => new Tuple(int.Parse(p.Split(':')[0]), int.Parse(p.Split(':')[1]), int.Parse(p.Split(':')[2]))); } this.sqlServerConnStr = sqlServerConnStr; if (!string.IsNullOrEmpty(rawProductNameToPosProductNameStr)) { rawProductNameToPosProductNameStr.Split(';') .Where(w => !string.IsNullOrEmpty(w)) .Select(p => new KeyValuePair(p.Split(':')[0], p.Split(':')[1])).ToList() .ForEach(v => rawProductNameToPosProductNameMapping.Add(v.Key, v.Value)); } } #endregion public void Init() { if (initialized) return; foreach (var fdcPumpController in fdcPumpControllers) { fdcPumpController.OnStateChange += (s, a) => { var pump = s as IFdcPumpController; try { if (a.NewPumpState == LogicalDeviceState.FDC_READY) { /* indicate for nozzle if replaced back */ var sizeLevelNozzleIdsOnPump = SiteConfigUtility.Default.GetSiteLevelNozzleIdsByPumpId(pump.PumpId); if (!sizeLevelNozzleIdsOnPump.Any()) { logger.Info("Could not found any site level nozzle ids for pump: " + pump.PumpId); return; } using (var posSqlConnection = new SqlConnection(this.sqlServerConnStr)) { try { /* idle would not carry nozzle id, so here reset all nozzles on target pump.*/ var setPumpOnIdleCommand = new SqlCommand(sizeLevelNozzleIdsOnPump.Select(siteLevelNozzleId => { SqliteDbContext dbContext = new SqliteDbContext(); //var lastTrx = dbContext.PumpTransactionModels.OrderByDescending(f => f.SaleEndTime) // .FirstOrDefault(f => f.PumpId == pump.PumpId && f.LogicalNozzleId == SiteConfigUtility.Default.GetNozzleLogicalIdBySiteLevelNozzleId(siteLevelNozzleId)); //var totalizer = // new Tuple((float)((lastTrx?.VolumeTotalizer ?? 0) * Math.Pow(10, 2)), // (float)((lastTrx?.AmountTotalizer ?? 0) * Math.Pow(10, 2))); //return // string.Format( // "Update jy_info set [status] = '{1}', qty=0, amount=0, fzqty='{2}', fzamount={3}" + // " where jihao = {0}", siteLevelNozzleId, 'F', // totalizer.Item1, totalizer.Item2); return string.Format( "Update jy_info set [status] = '{1}', qty=0, amount=0" + " where jihao = {0}", siteLevelNozzleId, 'F'); }).Aggregate((acc, n) => acc + " " + n), posSqlConnection); logger.Debug("setPumpOnIdleCommand(via Fdc): " + setPumpOnIdleCommand.CommandText); posSqlConnection.Open(); setPumpOnIdleCommand.ExecuteNonQuery(); } catch (Exception ex) { logger.Error("executing setPumpOnIdleCommand(via Fdc) failed, exception detail: " + ex); } } } else if (a.NewPumpState == LogicalDeviceState.FDC_CALLING) { using (var posSqlConnection = new SqlConnection(this.sqlServerConnStr)) { try { var operatingNozzleLogicalId = a?.StateChangedNozzles?.FirstOrDefault()?.LogicalId ?? (byte)0; var rawProductNo = nozzleProductConfig.First(f => f.PumpId == pump.PumpId && f.NozzleLogicalId == operatingNozzleLogicalId).ProductBarcode; var gradeFriendlyName = rawProductNameToPosProductNameMapping[rawProductNo.ToString()]; //var updatePumpOnFuelingCommand = // new SqlCommand(string.Format("Update jy_info set [status] = '{1}', youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}'" // , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0), // 'B', // gradeFriendlyName, // a.Transaction.Volumn, // a.Transaction.Amount), // posSqlConnection); var updatePumpOnFuelingCommand = new SqlCommand(string.Format("Update jy_info set [status] = '{1}' where jihao = '{0}'" , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, operatingNozzleLogicalId), 'B', gradeFriendlyName), posSqlConnection); logger.Debug("updatePumpOnFuelingCommand: " + updatePumpOnFuelingCommand.CommandText); posSqlConnection.Open(); updatePumpOnFuelingCommand.ExecuteNonQuery(); } catch (Exception ex) { logger.Error("executing updatePumpOnFuelingCommand failed, exception detail: " + ex); } } } } catch (Exception exx) { } }; fdcPumpController.OnCurrentFuellingStatusChange += async (s, a) => { try { var pump = s as IFdcPumpController; if (!a.Transaction.Finished) { //logger.Info("Start to query accumulator"); //var acc = await pump.QueryTotalizerAsync(a.Transaction.Nozzle.LogicalId); //logger.Info($"Amount accumulator: {acc.Item1}, Volume accumulator: {acc.Item2}"); using (var posSqlConnection = new SqlConnection(this.sqlServerConnStr)) { try { var rawProductNo = nozzleProductConfig.First(f => f.PumpId == pump.PumpId && f.NozzleLogicalId == a.Transaction.Nozzle.LogicalId).ProductBarcode; var gradeFriendlyName = rawProductNameToPosProductNameMapping[rawProductNo.ToString()]; //var updatePumpOnFuelingCommand = // new SqlCommand(string.Format("Update jy_info set [status] = '{1}', youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}'" // , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0), // 'B', // gradeFriendlyName, // a.Transaction.Volumn, // a.Transaction.Amount), // posSqlConnection); var updatePumpOnFuelingCommand = new SqlCommand(string.Format("Update jy_info set youpin = N'{2}', qty= {3}, amount= {4}, fzqty= {5}, fzamount= {6} where jihao = '{0}'" , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0), "", gradeFriendlyName, a.Transaction.Volumn / Math.Pow(10, pump.VolumeDecimalDigits), a.Transaction.Amount / Math.Pow(10, pump.AmountDecimalDigits), 0,//acc.Item2 / Math.Pow(10, pump.VolumeDecimalDigits), 0),//acc.Item1 / Math.Pow(10, pump.AmountDecimalDigits)), posSqlConnection); logger.Debug("updatePumpOnFuelingCommand: " + updatePumpOnFuelingCommand.CommandText); posSqlConnection.Open(); updatePumpOnFuelingCommand.ExecuteNonQuery(); } catch (Exception ex) { logger.Error("executing updatePumpOnFuelingCommand failed, exception detail: " + ex); } } } else { logger.Info("Filling done, start to query accumulator"); var acc = await pump.QueryTotalizerAsync(a.Transaction.Nozzle.LogicalId); logger.Info($"Amount accumulator: {acc.Item1}, Volume accumulator: {acc.Item2}"); var posSqlConnection = new SqlConnection(this.sqlServerConnStr); using (posSqlConnection) { try { var rawProductNo = nozzleProductConfig.First(f => f.PumpId == pump.PumpId && f.NozzleLogicalId == a.Transaction.Nozzle.LogicalId).ProductBarcode; var gradeFriendlyName = rawProductNameToPosProductNameMapping[rawProductNo.ToString()]; //var totalizer = SiteConfigUtility.Default.GetTotalizer(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id); var updateFuelingTrxDoneCommand = new SqlCommand( string.Format( "insert xiaofei2 (jihao, youpin, qty, danjia, amount, xf_date, xf_time, liushuino, fzqty, fzamount)" + " values({0}, N'{1}', {2}, {3}, {4}, '{5}', '{6}', {7}, {8}, {9})", SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a.Transaction.Nozzle.LogicalId), gradeFriendlyName, a.Transaction.Volumn / Math.Pow(10, pump.VolumeDecimalDigits), a.Transaction.Price / Math.Pow(10, pump.PriceDecimalDigits), a.Transaction.Amount / Math.Pow(10, pump.AmountDecimalDigits), DateTime.Now.Date.ToString("yyyy-MM-dd"), DateTime.Now.ToString("HH:mm:ss"), a.Transaction.SequenceNumberGeneratedOnPhysicalPump, acc.Item2 / Math.Pow(10, pump.VolumeDecimalDigits), acc.Item1 / Math.Pow(10, pump.AmountDecimalDigits)), posSqlConnection); logger.Info("updateFuelingTrxDoneCommand: " + updateFuelingTrxDoneCommand.CommandText); posSqlConnection.Open(); updateFuelingTrxDoneCommand.ExecuteNonQuery(); } catch (Exception ex) { logger.Error("executing updateFuelingTrxDoneCommand failed, exception detail: " + ex); } } using (var sqlConnection = new SqlConnection(this.sqlServerConnStr)) { try { //var rawProductNo = nozzleProductConfig.First(f => f.PumpId == pump.PumpId // && f.NozzleLogicalId == operatingNozzleLogicalId).ProductBarcode; //var gradeFriendlyName = rawProductNameToPosProductNameMapping[rawProductNo.ToString()]; //var updatePumpOnFuelingCommand = // new SqlCommand(string.Format("Update jy_info set [status] = '{1}', youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}'" // , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0), // 'B', // gradeFriendlyName, // a.Transaction.Volumn, // a.Transaction.Amount), // posSqlConnection); var updatePumpOnFuelingCommand = new SqlCommand(string.Format("Update jy_info set fzqty = '{1}', fzamount={2} where jihao = '{0}'" , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId( pump.PumpId, a.Transaction.Nozzle.LogicalId), acc.Item2 / Math.Pow(10, pump.VolumeDecimalDigits), acc.Item1 / Math.Pow(10, pump.AmountDecimalDigits)), sqlConnection); logger.Debug("updatePumpOnFuelingCommand: " + updatePumpOnFuelingCommand.CommandText); sqlConnection.Open(); updatePumpOnFuelingCommand.ExecuteNonQuery(); } catch (Exception ex) { logger.Error("executing updatePumpOnFuelingCommand failed, exception detail: " + ex); } } } } catch (Exception exxx) { } }; } #region table [jyjpz] try { var siteNozzlesDataTableInFcDb = new DataTable(); siteNozzlesDataTableInFcDb.Columns.Add("pumpid"); siteNozzlesDataTableInFcDb.Columns.Add("HoseLogicalId"); siteNozzlesDataTableInFcDb.Columns.Add("productNo"); foreach (var fdcPumpController in this.fdcPumpControllers) { foreach (var nozzle in fdcPumpController.Nozzles) { var bindProduct = nozzleProductConfig.FirstOrDefault(n => n.PumpId == fdcPumpController.PumpId && n.NozzleLogicalId == nozzle.LogicalId); var fuelPrice = nozzle.RealPriceOnPhysicalPump == null ? 0 : (nozzle.RealPriceOnPhysicalPump.Value / Math.Pow(10, fdcPumpController.PriceDecimalDigits)); var newRow = siteNozzlesDataTableInFcDb.NewRow();//.Rows.Add( new DataRow) newRow["pumpid"] = fdcPumpController.PumpId; newRow["HoseLogicalId"] = nozzle.LogicalId; newRow["productNo"] = bindProduct.ProductBarcode; siteNozzlesDataTableInFcDb.Rows.Add(newRow); } } // SinoChem defined a id of: 'jiHao' which stands for a physical pump(2 sides). siteNozzlesDataTableInFcDb.Columns.Add(new DataColumn("jiHao")); // SinoChem need another id of site overall id for a nozzle, start from 1. // we cacualte this based on jiHao and HostLogicalId siteNozzlesDataTableInFcDb.Columns.Add(new DataColumn("siteLevelNozzleId")); // as doc defined, side A is 0, side B is 1. siteNozzlesDataTableInFcDb.Columns.Add(new DataColumn("sideId")); var siteNozzlesDataRowsInFcDb = siteNozzlesDataTableInFcDb.Rows.Cast(); foreach (var row in siteNozzlesDataRowsInFcDb) { var pumpId = int.Parse(row["pumpid"].ToString()); var possibleSideA = this.pumpSidePairs.FirstOrDefault(p => p.Item1 == pumpId); Tuple possibleSideB = null; if (possibleSideA != null) { row["sideId"] = "0"; row["jiHao"] = possibleSideA.Item3; } else if ((possibleSideB = this.pumpSidePairs.FirstOrDefault(p => p.Item2 == pumpId)) != null) { row["sideId"] = "1"; row["jiHao"] = possibleSideB.Item3; } else { throw new ArgumentException("Pump with pumpId: " + pumpId + " is neither side A nor side B, pls check Side definition config file."); } } this.sortedSiteNozzlesDataRows = siteNozzlesDataRowsInFcDb.OrderBy(r => int.Parse(r["jiHao"].ToString())) .ThenBy(r => r["sideId"]) .ThenBy(r => r["HoseLogicalId"]); SiteConfigUtility.Default.UpdateLatestSiteConfig(this.sortedSiteNozzlesDataRows); logger.Debug("sortedSiteNozzlesDataRows: \r\n"); // finally we get the ordered sequence, now give them sitelevel nozzle id. for (var i = 0; i < sortedSiteNozzlesDataRows.Count(); i++) { var oneRow = sortedSiteNozzlesDataRows.ElementAt(i); oneRow["siteLevelNozzleId"] = i + 1; logger.Debug(" JiHao: " + oneRow["jiHao"] + ", sideId: " + oneRow["sideId"] + ", nozzleLogicalId: " + oneRow["HoseLogicalId"] + ", siteLevelNozzleId: " + oneRow["siteLevelNozzleId"] + ", pumpId: " + oneRow["pumpid"] + ", productNo: " + oneRow["productNo"] //+ ", hoseName: " + oneRow["hosename"] ); if (nozzleRemappingPairs != null) { var pumpId = int.Parse(oneRow["pumpid"].ToString()); var fusionHoseLogicalId = int.Parse(oneRow["HoseLogicalId"].ToString()); var matched = nozzleRemappingPairs.FirstOrDefault(m => m.Item1 == pumpId && m.Item2 == fusionHoseLogicalId); if (matched != null) { logger.Debug("Found nozzleRemappingPairs for pumpId: " + pumpId + " with HoseLogicalId: " + fusionHoseLogicalId + ", " + "will remapping the bound siteLevelNozzleId from: " + oneRow["siteLevelNozzleId"] + " to: " + matched.Item3); oneRow["siteLevelNozzleId"] = matched.Item3; } } } // for now, always overwrite the whole table in POS database without further check for rows or columns level. using (var posConn = new SqlConnection(this.sqlServerConnStr)) { /* configInFcDb structure like below */ //" JiHao: " + oneRow["jiHao"] + ", sideId: " + oneRow["sideId"] + //", nozzleLogicalId: " + oneRow["HoseLogicalId"] + //", siteLevelNozzleId: " + oneRow["siteLevelNozzleId"] string bulkInsertCmd = sortedSiteNozzlesDataRows.Select( r => string.Format("Select {0}, '{1}', {2}, {3}", r["jiHao"], r["sideId"], r["HoseLogicalId"], r["siteLevelNozzleId"])).Aggregate((acc, n) => acc + " Union all " + n); var truncateAndInsertCmd = new SqlCommand( "TRUNCATE table jyjpz; INSERT jyjpz (jihao, abtype, qianghao, luojiqh) " + bulkInsertCmd, posConn); logger.Info("truncateAndInsertSiteConfigCmd: " + truncateAndInsertCmd.CommandText); posConn.Open(); truncateAndInsertCmd.ExecuteNonQuery(); //return true; } } catch (Exception ex) { logger.Error("PerformCompareAndUpdate exceptioned, detail: " + ex); } #endregion #region table jy_info foreach (var nozzle in sortedSiteNozzlesDataRows)//.Select(n => n["siteLevelNozzleId"])) { var siteLevelNozzleId = nozzle["siteLevelNozzleId"]; var pumpId = int.Parse(nozzle["pumpid"].ToString()); var pump = this.fdcPumpControllers.First(f => f.PumpId == pumpId); var nozzleLogicalId = int.Parse(nozzle["HoseLogicalId"].ToString()); var productNo = nozzle["productNo"].ToString(); using ( var posSqlConnection = new SqlConnection(this.sqlServerConnStr)) { try { var gradeFriendlyName = rawProductNameToPosProductNameMapping[productNo];// Translator.GetFriendlyGradeName(SiteConfigUtility.Default.GetGradeName(siteLevelNozzleId)); SqliteDbContext dbContext = new SqliteDbContext(); var lastTrx = dbContext.PumpTransactionModels.OrderByDescending(f => f.SaleEndTime) .FirstOrDefault(f => f.PumpId == pumpId && f.LogicalNozzleId == nozzleLogicalId); var totalizer = new Tuple((float)((lastTrx?.VolumeTotalizer / Math.Pow(10, pump.VolumeDecimalDigits) ?? 0)), (float)((lastTrx?.AmountTotalizer / Math.Pow(10, pump.AmountDecimalDigits) ?? 0) * Math.Pow(10, 2))); var setPumpOnStartingCommand = new SqlCommand(string.Format("if not exists(select * from jy_info where jihao={0})" + " BEGIN" + " insert jy_info (jihao, [status], youpin, qty, amount, fzqty, fzamount) values({0}, '{1}', N'{2}', 0, 0, '{3}', {4})" + " END", siteLevelNozzleId, 'F', gradeFriendlyName, totalizer.Item1, totalizer.Item2), posSqlConnection); logger.Debug("initTable_jy_info_Command: " + setPumpOnStartingCommand.CommandText); posSqlConnection.Open(); setPumpOnStartingCommand.ExecuteNonQuery(); } catch (Exception ex) { logger.Error("executing initTable_jy_info_Command failed for siteLevelNozzleId: " + siteLevelNozzleId + ", exception detail: " + ex); throw; } } } #endregion initialized = true; } private void PrepareInsertTransToXiaofei2() { } private Tuple GetAccumulator(IFdcPumpController pumpController, byte logicalNozzleId) { return pumpController.QueryTotalizerAsync(logicalNozzleId).Result; } } }