AppBase.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. using Applications.FDC;
  2. using Edge.Core.Database;
  3. using Edge.Core.Processor;
  4. using Edge.Core.IndustryStandardInterface.Pump;
  5. using Microsoft.Extensions.DependencyInjection;
  6. using Microsoft.Extensions.Logging;
  7. using Microsoft.Extensions.Logging.Abstractions;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Data;
  11. using System.Data.Common;
  12. using System.Linq;
  13. using System.Text;
  14. using Wayne.FDCPOSLibrary;
  15. using Edge.Core.Configuration;
  16. namespace Applications.PumpInfoToSinoChemPosSqlServerOrMySql
  17. {
  18. public abstract class AppBase
  19. {
  20. protected ILogger logger = NullLogger.Instance;
  21. protected FdcServerHostApp fdcServerHostApp;// = Configurator.Default.NozzleProductConfiguration.Mapping;
  22. protected AppConfigV1 appConfig;
  23. #region App config
  24. public class AppConfigV1
  25. {
  26. public string DatabaseConnStr { get; set; }
  27. public string JiaYouJiPeiZhi_TableName { get; set; }
  28. public List<YouJiConfigV1> YouJiConfigs { get; set; }
  29. }
  30. /// <summary>
  31. /// 对应一台物理油机
  32. /// </summary>
  33. public class YouJiConfigV1
  34. {
  35. /// <summary>
  36. /// 油机编号,由1开始编号
  37. /// 假设某个油站有四台油机, 则此号码分别为1、2、3、4
  38. /// </summary>
  39. public byte JiHao { get; set; }
  40. public List<YouMianConfigV1> YouMianConfigs { get; set; }
  41. }
  42. /// <summary>
  43. /// 每台物理油机有两个油面(A面或B面)
  44. /// </summary>
  45. public class YouMianConfigV1
  46. {
  47. /// <summary>
  48. /// 0 or 1, 0=A面 1=B面
  49. /// </summary>
  50. public int YouMianBianHao { get; set; }
  51. public List<YouQiangConfigV1> YouQiangConfigs { get; set; }
  52. }
  53. public class YouQiangConfigV1
  54. {
  55. /// <summary>
  56. /// 每台油面下的枪号,由1开始编号,作用范围是一个面,即换面则重置,重新从1开始。
  57. /// 如某个面上有2把枪,则此编号为1、2,另外一个面上有3把枪,则此编号为1、2、3.
  58. /// </summary>
  59. public int QiangHao { get; set; }
  60. /// <summary>
  61. /// 枪号,对应"加油流水表"和"油枪状态表"的枪号(jihao),整个油站的所有枪号由1开始编号
  62. ///
  63. /// 应该与FDC SERVER中的site level nozzle id相匹配对应。
  64. /// </summary>
  65. public int LuoJiQiangHao { get; set; }
  66. }
  67. #endregion
  68. protected AppBase(AppConfigV1 appConfig, IServiceProvider services)
  69. {
  70. this.appConfig = appConfig;
  71. var loggerFactory = services.GetRequiredService<ILoggerFactory>();
  72. this.logger = loggerFactory.CreateLogger("Application");
  73. }
  74. /// <summary>
  75. /// Truncate table jyjpz and jy_info, and then re-insert site nozzles info to them.
  76. /// </summary>
  77. /// <param name="dbConnCreator"></param>
  78. /// <param name="dbCommandCreator"></param>
  79. /// <returns></returns>
  80. protected bool ReCreatePumpLayoutToDatabase(Func<DbConnection> dbConnCreator,
  81. Func<DbCommand> dbCommandCreator)
  82. {
  83. try
  84. {
  85. #region truncate table [jyjpz] and [jy_info], and then insert data into table [jyjpz]
  86. // for now, always overwrite the whole table in POS database without further check for rows or columns level.
  87. using (var posConn = dbConnCreator())
  88. {
  89. /* configInFcDb structure like below */
  90. //" JiHao: " + oneRow["jiHao"] + ", sideId: " + oneRow["sideId"] +
  91. //", nozzleLogicalId: " + oneRow["HoseLogicalId"] +
  92. //", siteLevelNozzleId: " + oneRow["siteLevelNozzleId"]
  93. string bulkInsertCmd = "";
  94. const string sqlConcateAppendix = "Union all ";
  95. foreach (var youJiConfig in this.appConfig.YouJiConfigs)
  96. {
  97. foreach (var youMianConfig in youJiConfig.YouMianConfigs)
  98. {
  99. foreach (var youQiangConfig in youMianConfig.YouQiangConfigs)
  100. {
  101. bulkInsertCmd +=
  102. $"Select {youJiConfig.JiHao}, '{youMianConfig.YouMianBianHao}', {youQiangConfig.QiangHao}, {youQiangConfig.LuoJiQiangHao} {sqlConcateAppendix}";
  103. }
  104. }
  105. }
  106. var truncateAndInsertCmd = dbCommandCreator();
  107. truncateAndInsertCmd.Connection = posConn;
  108. truncateAndInsertCmd.CommandText =
  109. "TRUNCATE table " + this.appConfig.JiaYouJiPeiZhi_TableName + "; TRUNCATE table jy_info; INSERT " + this.appConfig.JiaYouJiPeiZhi_TableName + " (jihao, abtype, qianghao, luojiqh) " + bulkInsertCmd.Substring(0, bulkInsertCmd.Length - sqlConcateAppendix.Length);
  110. logger.LogInformation("truncateAndInsertSiteConfigCmd: " + truncateAndInsertCmd.CommandText);
  111. posConn.Open();
  112. truncateAndInsertCmd.ExecuteNonQuery();
  113. //return true;
  114. };
  115. #endregion
  116. #region insert data into table [jy_info]
  117. //foreach (var nozzle in sortedSiteNozzlesDataRows)//.Select(n => n["siteLevelNozzleId"]))
  118. //{
  119. //var siteLevelNozzleId = nozzle["siteLevelNozzleId"];
  120. foreach (var nozzle in fdcServerHostApp.FdcPumpControllers.SelectMany(c => c.Nozzles))
  121. {
  122. //var pumpId = int.Parse(nozzle["pumpid"].ToString());
  123. //var pump = fdcServerHostApp.FdcPumpControllers.First(f => f.PumpId == pumpId);
  124. //var nozzleLogicalId = int.Parse(nozzle["HoseLogicalId"].ToString());
  125. using (var posSqlConnection = dbConnCreator())
  126. {
  127. var bindingNozzleExtraInfo = this.fdcServerHostApp.GetNozzleExtraInfos()?.FirstOrDefault(f => f.PumpId == nozzle.PumpId
  128. && f.NozzleLogicalId == nozzle.LogicalId);
  129. try
  130. {
  131. //var gradeFriendlyName = rawProductNameToPosProductNameMapping[productNo];// Translator.GetFriendlyGradeName(SiteConfigUtility.Default.GetGradeName(siteLevelNozzleId));
  132. //SqliteDbContext dbContext = new SqliteDbContext();
  133. //var lastTrx = dbContext.PumpTransactionModels.OrderByDescending(f => f.SaleEndTime)
  134. // .FirstOrDefault(f => f.PumpId == pumpId && f.LogicalNozzleId == nozzleLogicalId);
  135. var lastTrx = fdcServerHostApp.GetAvailableFuelSaleTrxsWithDetailsAsync(nozzle.PumpId, nozzle.LogicalId, 1).Result.FirstOrDefault();
  136. var totalizer = fdcServerHostApp.GetFuelPointTotalsAsync(nozzle.PumpId, nozzle.LogicalId).Result;
  137. //new Tuple<float, float>((float)((lastTrx?.VolumeTotalizer / Math.Pow(10, pump.VolumeDecimalDigits) ?? 0)),
  138. // (float)((lastTrx?.AmountTotalizer / Math.Pow(10, pump.AmountDecimalDigits) ?? 0) * Math.Pow(10, 2)));
  139. var setPumpOnStartingCommand = dbCommandCreator();
  140. setPumpOnStartingCommand.Connection = posSqlConnection;
  141. setPumpOnStartingCommand.CommandText = this.InitTable_jy_info_SqlCommand(
  142. (bindingNozzleExtraInfo?.SiteLevelNozzleId ?? -1).ToString(),
  143. (bindingNozzleExtraInfo?.ProductName ?? "undefined"),
  144. totalizer.Item2, totalizer.Item1);
  145. logger.LogDebug("initTable_jy_info_Command: " + setPumpOnStartingCommand.CommandText);
  146. posSqlConnection.Open();
  147. setPumpOnStartingCommand.ExecuteNonQuery();
  148. }
  149. catch (Exception ex)
  150. {
  151. logger.LogError("executing initTable_jy_info_Command failed for siteLevelNozzleId: " + (bindingNozzleExtraInfo?.SiteLevelNozzleId ?? -1) + ", exception detail: " + ex);
  152. throw;
  153. }
  154. }
  155. }
  156. #endregion
  157. }
  158. catch (Exception ex)
  159. {
  160. logger.LogError("PerformCompareAndUpdate exceptioned, detail: " + ex);
  161. return false;
  162. }
  163. return true;
  164. }
  165. protected virtual string InitTable_jy_info_SqlCommand(string siteLevelNozzleId, string gradeFriendlyName,
  166. double totalizerVol, double totalizerAmt)
  167. {
  168. return string.Format("if not exists(select * from jy_info where jihao={0})" +
  169. " BEGIN" +
  170. " insert jy_info (jihao, status, youpin, qty, amount, fzqty, fzamount) values({0}, '{1}', N'{2}', 0, 0, '{3}', {4})" +
  171. " END",
  172. siteLevelNozzleId,
  173. 'F',
  174. gradeFriendlyName,
  175. totalizerVol, totalizerAmt);
  176. }
  177. /// <summary>
  178. /// attach pump event for inserting correlated data to database tables.
  179. /// </summary>
  180. /// <param name="dbConnCreator"></param>
  181. /// <param name="dbCommandCreator"></param>
  182. /// <returns></returns>
  183. protected bool HandlePumpStateChangeToDatabase(Func<DbConnection> dbConnCreator,
  184. Func<DbCommand> dbCommandCreator)
  185. {
  186. var siteLevelNozzldIdsInAppConfig = this.appConfig.YouJiConfigs.SelectMany(youJiConfig => youJiConfig.YouMianConfigs.SelectMany(youMianConfig => youMianConfig.YouQiangConfigs))
  187. .Select(youQiangConfig => youQiangConfig.LuoJiQiangHao);
  188. var siteLevelNozzldIdsInFdcServerApp = this.fdcServerHostApp.GetNozzleExtraInfos().Where(ri => ri.SiteLevelNozzleId.HasValue).Select(ri => ri.SiteLevelNozzleId.Value);
  189. var missedInFdcServerApp = siteLevelNozzldIdsInAppConfig.Except(siteLevelNozzldIdsInFdcServerApp);
  190. var missedInAppConfig = siteLevelNozzldIdsInFdcServerApp.Except(siteLevelNozzldIdsInAppConfig);
  191. if (missedInFdcServerApp.Any())
  192. {
  193. throw new ArgumentException("油枪(以此App中 全站枪号 为标识): "
  194. + missedInFdcServerApp.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + " 未配置于FdcServerApp中, 请配置后再试");
  195. }
  196. if (missedInAppConfig.Any())
  197. {
  198. throw new ArgumentException("油枪(以 FdcServerApp中的全站枪号 为标识): "
  199. + missedInAppConfig.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + " 未配置于此App中, 请配置后再试");
  200. }
  201. fdcServerHostApp.OnStateChange += (s, a) =>
  202. {
  203. var pump = s as IFdcPumpController;
  204. try
  205. {
  206. if (a.NewPumpState == LogicalDeviceState.FDC_READY)
  207. {
  208. /* indicate for nozzle if replaced back */
  209. var sizeLevelNozzleIdsOnPump = this.fdcServerHostApp.GetNozzleExtraInfos().Where(ri => ri.PumpId == pump.PumpId && ri.SiteLevelNozzleId.HasValue).Select(ri => ri.SiteLevelNozzleId.Value);
  210. if (!sizeLevelNozzleIdsOnPump.Any())
  211. {
  212. logger.LogInformation("Could not found any site level nozzle id defined on pump: " + pump.PumpId);
  213. return;
  214. }
  215. using (var posSqlConnection = dbConnCreator())
  216. {
  217. try
  218. {
  219. /* idle would not carry nozzle id, so here reset all nozzles on target pump.*/
  220. var setPumpOnIdleCommand = dbCommandCreator();
  221. setPumpOnIdleCommand.Connection = posSqlConnection;
  222. setPumpOnIdleCommand.CommandText = sizeLevelNozzleIdsOnPump.Select(
  223. siteLevelNozzleId =>
  224. string.Format(
  225. "Update jy_info set status = '{1}', qty=0, amount=0 where jihao = {0};", siteLevelNozzleId, 'F')
  226. ).Aggregate((acc, n) => acc + " " + n);
  227. logger.LogDebug("setPumpOnIdleCommand(via Fdc): " + setPumpOnIdleCommand.CommandText);
  228. posSqlConnection.Open();
  229. setPumpOnIdleCommand.ExecuteNonQuery();
  230. }
  231. catch (Exception ex)
  232. {
  233. logger.LogError("executing setPumpOnIdleCommand(via Fdc) failed, exception detail: " + ex);
  234. }
  235. }
  236. }
  237. else if (a.NewPumpState == LogicalDeviceState.FDC_CALLING)
  238. {
  239. using (var posSqlConnection = dbConnCreator())
  240. {
  241. try
  242. {
  243. var operatingNozzleLogicalId = a?.StateChangedNozzles?.FirstOrDefault()?.LogicalId ?? (byte)0;
  244. var bindingNozzleExtraInfo = this.fdcServerHostApp.GetNozzleExtraInfos()?.FirstOrDefault(f => f.PumpId == pump.PumpId
  245. && f.NozzleLogicalId == operatingNozzleLogicalId);
  246. //var updatePumpOnFuelingCommand =
  247. // new SqlCommand(string.Format("Update jy_info set [status] = '{1}', youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}'"
  248. // , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0),
  249. // 'B',
  250. // gradeFriendlyName,
  251. // a.Transaction.Volumn,
  252. // a.Transaction.Amount),
  253. // posSqlConnection);
  254. var updatePumpOnFuelingCommand = dbCommandCreator();
  255. updatePumpOnFuelingCommand.Connection = posSqlConnection;
  256. updatePumpOnFuelingCommand.CommandText =
  257. string.Format("Update jy_info set status = '{1}' where jihao = '{0}';"
  258. , (bindingNozzleExtraInfo?.SiteLevelNozzleId ?? -1),
  259. 'B',
  260. (bindingNozzleExtraInfo?.ProductName ?? "undefined"));
  261. logger.LogDebug("updatePumpOnFuelingCommand: " + updatePumpOnFuelingCommand.CommandText);
  262. posSqlConnection.Open();
  263. updatePumpOnFuelingCommand.ExecuteNonQuery();
  264. }
  265. catch (Exception ex)
  266. {
  267. logger.LogError("executing updatePumpOnFuelingCommand failed, exception detail: " + ex);
  268. }
  269. }
  270. }
  271. }
  272. catch (Exception exx) { }
  273. };
  274. fdcServerHostApp.OnCurrentFuellingStatusChange += async (s, a) =>
  275. {
  276. try
  277. {
  278. var pump = s as IFdcPumpController;
  279. if (!a.Transaction.Finished)
  280. {
  281. //add some random drops to reduce the operations in db, this is for performance improve.
  282. if (DateTime.Now.Millisecond % 2 == 0 || DateTime.Now.Millisecond % 3 == 0) return;
  283. using (var posSqlConnection = dbConnCreator())
  284. {
  285. try
  286. {
  287. var bindingNozzleExtraInfo = this.fdcServerHostApp.GetNozzleExtraInfos().FirstOrDefault(f => f.PumpId == pump.PumpId
  288. && f.NozzleLogicalId == a.Transaction.Nozzle.LogicalId);
  289. //var updatePumpOnFuelingCommand =
  290. // new SqlCommand(string.Format("Update jy_info set [status] = '{1}', youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}'"
  291. // , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0),
  292. // 'B',
  293. // gradeFriendlyName,
  294. // a.Transaction.Volumn,
  295. // a.Transaction.Amount),
  296. // posSqlConnection);
  297. var updatePumpOnFuelingCommand = dbCommandCreator();
  298. updatePumpOnFuelingCommand.Connection = posSqlConnection;
  299. updatePumpOnFuelingCommand.CommandText =
  300. string.Format("Update jy_info set youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}';"
  301. , (bindingNozzleExtraInfo?.SiteLevelNozzleId ?? -1),
  302. "",
  303. (bindingNozzleExtraInfo?.ProductName ?? "undefined"),
  304. a.Transaction.Volumn / Math.Pow(10, pump.VolumeDecimalDigits),
  305. a.Transaction.Amount / Math.Pow(10, pump.AmountDecimalDigits));
  306. logger.LogDebug("updatePumpOnFuelingCommand: " + updatePumpOnFuelingCommand.CommandText);
  307. posSqlConnection.Open();
  308. updatePumpOnFuelingCommand.ExecuteNonQuery();
  309. }
  310. catch (Exception ex)
  311. {
  312. logger.LogError("executing updatePumpOnFuelingCommand failed, exception detail: " + ex);
  313. }
  314. }
  315. }
  316. else
  317. {
  318. #region totalizer value reading
  319. double volumeTotalizerValue = 0, amountTotalizerValue = 0;
  320. if (a.Transaction.VolumeTotalizer.HasValue)
  321. {
  322. /*indicates this kind of device will carry totalizer with trx, use it directly*/
  323. volumeTotalizerValue = a.Transaction.VolumeTotalizer.Value / Math.Pow(10, pump.VolumeDecimalDigits);
  324. amountTotalizerValue = a.Transaction.AmountTotalizer ?? -1;
  325. }
  326. else
  327. {
  328. /*indicates this kind of device will NOT carry totalizer with trx,
  329. * have to query it*/
  330. var queried = await fdcServerHostApp.GetFuelPointTotalsAsync(pump.PumpId, a.Transaction.Nozzle.LogicalId);
  331. volumeTotalizerValue = queried.Item2;
  332. amountTotalizerValue = queried.Item1;
  333. }
  334. #endregion
  335. var bindingNozzleExtraInfo = this.fdcServerHostApp.GetNozzleExtraInfos()?.FirstOrDefault(f => f.PumpId == pump.PumpId
  336. && f.NozzleLogicalId == a.Transaction.Nozzle.LogicalId);
  337. var posSqlConnection = dbConnCreator();
  338. using (posSqlConnection)
  339. {
  340. try
  341. {
  342. //var rawProductNo = nozzleProductConfig.FirstOrDefault(f => f.PumpId == pump.PumpId
  343. // && f.NozzleLogicalId == a.Transaction.Nozzle.LogicalId)?.ProductBarcode;
  344. //var totalizer = SiteConfigUtility.Default.GetTotalizer(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id);
  345. var updateFuelingTrxDoneCommand = dbCommandCreator();
  346. updateFuelingTrxDoneCommand.Connection = posSqlConnection;
  347. updateFuelingTrxDoneCommand.CommandText =
  348. string.Format(
  349. "insert xiaofei2 (jihao, youpin, qty, danjia, amount, xf_date, xf_time, liushuino, fzqty, fzamount)" +
  350. " values({0}, N'{1}', {2}, {3}, {4}, '{5}', '{6}', {7}, {8}, {9})",
  351. (bindingNozzleExtraInfo?.SiteLevelNozzleId ?? -1),
  352. (bindingNozzleExtraInfo?.ProductName ?? "undefined"),
  353. a.Transaction.Volumn / Math.Pow(10, pump.VolumeDecimalDigits),
  354. a.Transaction.Price / Math.Pow(10, pump.PriceDecimalDigits),
  355. a.Transaction.Amount / Math.Pow(10, pump.AmountDecimalDigits),
  356. DateTime.Now.Date.ToString("yyyy-MM-dd"),
  357. DateTime.Now.ToString("HH:mm:ss"),
  358. a.Transaction.SequenceNumberGeneratedOnPhysicalPump,
  359. volumeTotalizerValue,
  360. amountTotalizerValue);
  361. logger.LogInformation("updateFuelingTrxDoneCommand: " + updateFuelingTrxDoneCommand.CommandText);
  362. posSqlConnection.Open();
  363. updateFuelingTrxDoneCommand.ExecuteNonQuery();
  364. }
  365. catch (Exception ex)
  366. {
  367. logger.LogError("executing updateFuelingTrxDoneCommand failed, exception detail: " + ex);
  368. }
  369. }
  370. using (var sqlConnection = dbConnCreator())
  371. {
  372. try
  373. {
  374. //var rawProductNo = nozzleProductConfig.First(f => f.PumpId == pump.PumpId
  375. // && f.NozzleLogicalId == operatingNozzleLogicalId).ProductBarcode;
  376. //var gradeFriendlyName = rawProductNameToPosProductNameMapping[rawProductNo.ToString()];
  377. //var updatePumpOnFuelingCommand =
  378. // new SqlCommand(string.Format("Update jy_info set [status] = '{1}', youpin = N'{2}', qty= {3}, amount= {4} where jihao = '{0}'"
  379. // , SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a?.Transaction?.Nozzle?.LogicalId ?? 0),
  380. // 'B',
  381. // gradeFriendlyName,
  382. // a.Transaction.Volumn,
  383. // a.Transaction.Amount),
  384. // posSqlConnection);
  385. var updatePumpOnFuelingCommand = dbCommandCreator();
  386. updatePumpOnFuelingCommand.Connection = sqlConnection;
  387. updatePumpOnFuelingCommand.CommandText =
  388. string.Format("Update jy_info set fzqty = '{1}', fzamount={2} where jihao = '{0}'",
  389. (bindingNozzleExtraInfo?.SiteLevelNozzleId ?? -1),
  390. volumeTotalizerValue,
  391. amountTotalizerValue);
  392. logger.LogDebug("updatePumpOnFuelingCommand: " + updatePumpOnFuelingCommand.CommandText);
  393. sqlConnection.Open();
  394. updatePumpOnFuelingCommand.ExecuteNonQuery();
  395. }
  396. catch (Exception ex)
  397. {
  398. logger.LogError("executing updatePumpOnFuelingCommand failed, exception detail: " + ex);
  399. }
  400. }
  401. }
  402. }
  403. catch (Exception exxx) { }
  404. };
  405. return true;
  406. }
  407. }
  408. }