CoreProfileHandler.cs 97 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  1. using Dapper;
  2. using EVCB_OCPP.Domain;
  3. using EVCB_OCPP.Domain.Models.Database;
  4. using EVCB_OCPP.Packet.Features;
  5. using EVCB_OCPP.Packet.Messages;
  6. using EVCB_OCPP.Packet.Messages.Core;
  7. using EVCB_OCPP.Packet.Messages.SubTypes;
  8. using EVCB_OCPP.WSServer.Dto;
  9. using EVCB_OCPP.WSServer.Service;
  10. using Newtonsoft.Json;
  11. using Newtonsoft.Json.Linq;
  12. using NLog;
  13. using OCPPPackage.Profiles;
  14. using OCPPServer.Protocol;
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Configuration;
  18. using System.Data;
  19. using System.Data.Entity;
  20. using System.Data.SqlClient;
  21. using System.Diagnostics;
  22. using System.Globalization;
  23. using System.Linq;
  24. using System.Threading.Tasks;
  25. namespace EVCB_OCPP.WSServer.Message
  26. {
  27. public class ID_CreditDeductResult
  28. {
  29. public int txId { set; get; }
  30. public string creditNo { set; get; }
  31. public bool deductResult { set; get; }
  32. public bool isDonateInvoice { set; get; }
  33. public decimal amount { set; get; }
  34. public string approvalNo { set; get; }
  35. }
  36. public class ID_ReaderStatus
  37. {
  38. public int ConnectorId { set; get; }
  39. public string creditNo { set; get; }
  40. public string SerialNo { set; get; }
  41. public int readerStatus { set; get; }
  42. public string VEMData { set; get; }
  43. public DateTime Timestamp { set; get; }
  44. }
  45. internal partial class ProfileHandler
  46. {
  47. static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
  48. string webConnectionString = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
  49. private OuterHttpClient httpClient = new OuterHttpClient();
  50. async internal Task<MessageResult> ExecuteCoreRequest(Actions action, ClientData session, IRequest request)
  51. {
  52. MessageResult result = new MessageResult() { Success = false };
  53. try
  54. {
  55. switch (action)
  56. {
  57. case Actions.DataTransfer:
  58. {
  59. DataTransferRequest _request = request as DataTransferRequest;
  60. var confirm = new DataTransferConfirmation() { status = DataTransferStatus.UnknownMessageId };
  61. if (_request.messageId == "ID_CreditDeductResult")
  62. {
  63. var creditDeductResult = JsonConvert.DeserializeObject<ID_CreditDeductResult>(_request.data);
  64. if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
  65. {
  66. var report = new
  67. {
  68. ChargeBoxId = session.ChargeBoxId,
  69. IsDonateInvoice = creditDeductResult.isDonateInvoice,
  70. CreditNo = creditDeductResult.creditNo,
  71. DeductResult = creditDeductResult.deductResult,
  72. SessionId = creditDeductResult.txId,
  73. ApprovalNo = creditDeductResult.approvalNo,
  74. TotalCost = creditDeductResult.amount,
  75. };
  76. var response = await httpClient.Post(GlobalConfig.TCC_API_URL + "prepare_issue_invoice", new Dictionary<string, string>()
  77. {
  78. { "PartnerId",session.CustomerId.ToString()}
  79. }, report, GlobalConfig.TCC_SALTKEY);
  80. logger.Debug(JsonConvert.SerializeObject(response));
  81. }
  82. confirm.status = DataTransferStatus.Accepted;
  83. confirm.data = JsonConvert.SerializeObject(new { txId = creditDeductResult.txId, creditNo = creditDeductResult.creditNo, msgId = _request.messageId });
  84. }
  85. if (_request.messageId == "ID_ReaderStatus")
  86. {
  87. if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
  88. {
  89. var preauth_status = JsonConvert.DeserializeObject<ID_ReaderStatus>(_request.data);
  90. var report = new
  91. {
  92. ChargeBoxId = session.ChargeBoxId,
  93. ConnectorId = preauth_status.ConnectorId,
  94. CreditNo = preauth_status.creditNo,
  95. ReaderStatus = preauth_status.readerStatus,
  96. SerialNo = preauth_status.SerialNo,
  97. VEMData = preauth_status.VEMData,
  98. Timestamp = preauth_status.Timestamp
  99. };
  100. var response = await httpClient.Post(GlobalConfig.TCC_API_URL + "preauth_status", new Dictionary<string, string>()
  101. {
  102. { "PartnerId",session.CustomerId.ToString()}
  103. }, report, GlobalConfig.TCC_SALTKEY);
  104. confirm.status = DataTransferStatus.Accepted;
  105. }
  106. }
  107. if (_request.messageId == "ID_OCMF")
  108. {
  109. JObject jo = JObject.Parse(_request.data);
  110. logger.Debug(string.Format("{0}\r\n{1}\r\n{2}", jo["txId"].Value<int>(), jo["dataString"].Value<string>(), jo["publicKey"].Value<string>()));
  111. using (var db = new MainDBContext())
  112. {
  113. db.OCMF.Add(new OCMF()
  114. {
  115. TransactionId = jo["txId"].Value<int>(),
  116. DataString = jo["dataString"].Value<string>(),
  117. PublicKey = jo["publicKey"].Value<string>()
  118. });
  119. await db.SaveChangesAsync();
  120. }
  121. confirm.status = DataTransferStatus.Accepted;
  122. confirm.data = JsonConvert.SerializeObject(new { txId = jo["txId"].Value<int>(), msgId = _request.messageId });
  123. }
  124. result.Message = confirm;
  125. result.Success = true;
  126. }
  127. break;
  128. case Actions.BootNotification:
  129. {
  130. Debug.WriteLine(DateTime.UtcNow);
  131. if (session.CustomerId == Guid.Empty)
  132. {
  133. result.Message = new BootNotificationConfirmation() { currentTime = DateTime.UtcNow, interval = 5, status = RegistrationStatus.Pending };
  134. }
  135. else
  136. {
  137. BootNotificationRequest _request = request as BootNotificationRequest;
  138. int heartbeat_interval = GlobalConfig.GetHEARTBEAT_INTERVAL();
  139. using (var db = new MainDBContext())
  140. {
  141. var _machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
  142. _machine.ChargeBoxSerialNumber = string.IsNullOrEmpty(_request.chargeBoxSerialNumber) ? string.Empty : _request.chargeBoxSerialNumber;
  143. _machine.ChargePointSerialNumber = string.IsNullOrEmpty(_request.chargePointSerialNumber) ? string.Empty : _request.chargePointSerialNumber;
  144. _machine.ChargePointModel = string.IsNullOrEmpty(_request.chargePointModel) ? string.Empty : _request.chargePointModel;
  145. _machine.ChargePointVendor = string.IsNullOrEmpty(_request.chargePointVendor) ? string.Empty : _request.chargePointVendor;
  146. _machine.FW_CurrentVersion = string.IsNullOrEmpty(_request.firmwareVersion) ? string.Empty : _request.firmwareVersion;
  147. //_machine.Iccid = string.IsNullOrEmpty(_request.iccid) ? string.Empty : _request.iccid;
  148. _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
  149. _machine.Imsi = string.IsNullOrEmpty(_request.imsi) ? string.Empty : _request.imsi;
  150. _machine.MeterSerialNumber = string.IsNullOrEmpty(_request.meterSerialNumber) ? string.Empty : _request.meterSerialNumber;
  151. _machine.MeterType = string.IsNullOrEmpty(_request.meterType) ? string.Empty : _request.meterType;
  152. db.SaveChanges();
  153. var configVaule = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.HeartbeatInterval)
  154. .Select(x => x.ConfigureSetting).FirstOrDefault();
  155. if (configVaule != null)
  156. {
  157. int.TryParse(configVaule, out heartbeat_interval);
  158. }
  159. }
  160. if (session.IsPending == true)
  161. {
  162. session.IsPending = false;
  163. }
  164. if (session.IsPending == null)
  165. {
  166. session.IsPending = true;
  167. }
  168. }
  169. result.Success = true;
  170. }
  171. break;
  172. case Actions.StatusNotification:
  173. {
  174. //只保留最新上報狀況
  175. StatusNotificationRequest _request = request as StatusNotificationRequest;
  176. int preStatus = 0;
  177. using (var db = new MainDBContext())
  178. {
  179. var _oldStatus = db.ConnectorStatus.Where(x => x.ChargeBoxId == session.ChargeBoxId
  180. && x.ConnectorId == _request.connectorId).AsNoTracking().FirstOrDefault();
  181. if (_oldStatus != null && (_request.status != (ChargePointStatus)_oldStatus.Status || _request.status == ChargePointStatus.Faulted))
  182. {
  183. preStatus = _oldStatus.Status;
  184. db.Configuration.AutoDetectChangesEnabled = false;
  185. db.Configuration.ValidateOnSaveEnabled = false;
  186. db.ConnectorStatus.Attach(_oldStatus);
  187. _oldStatus.CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow;
  188. _oldStatus.Status = (int)_request.status;
  189. _oldStatus.ChargePointErrorCodeId = (int)_request.errorCode;
  190. _oldStatus.ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info;
  191. _oldStatus.VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId;
  192. _oldStatus.VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode;
  193. db.Entry(_oldStatus).Property(x => x.CreatedOn).IsModified = true;
  194. db.Entry(_oldStatus).Property(x => x.Status).IsModified = true;
  195. db.Entry(_oldStatus).Property(x => x.ChargePointErrorCodeId).IsModified = true;
  196. db.Entry(_oldStatus).Property(x => x.ErrorInfo).IsModified = true;
  197. db.Entry(_oldStatus).Property(x => x.VendorId).IsModified = true;
  198. db.Entry(_oldStatus).Property(x => x.VendorErrorCode).IsModified = true;
  199. }
  200. if (_oldStatus == null)
  201. {
  202. var _currentStatus = new Domain.Models.Database.ConnectorStatus()
  203. {
  204. ChargeBoxId = session.ChargeBoxId,
  205. ConnectorId = (byte)_request.connectorId,
  206. CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
  207. Status = (int)_request.status,
  208. ChargePointErrorCodeId = (int)_request.errorCode,
  209. ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
  210. VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId,
  211. VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
  212. Id = Guid.NewGuid().ToString()
  213. };
  214. db.ConnectorStatus.Add(_currentStatus);
  215. }
  216. if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
  217. {
  218. db.MachineError.Add(new MachineError()
  219. {
  220. ConnectorId = (byte)_request.connectorId,
  221. CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
  222. Status = (int)_request.status,
  223. ChargeBoxId = session.ChargeBoxId,
  224. ErrorCodeId = (int)_request.errorCode,
  225. ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
  226. PreStatus = _oldStatus == null ? -1 : preStatus,
  227. VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
  228. VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId
  229. });
  230. }
  231. db.SaveChanges();
  232. }
  233. if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
  234. {
  235. var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
  236. var notification = businessService.NotifyFaultStatus(new ErrorDetails()
  237. {
  238. ChargeBoxId = session.ChargeBoxId,
  239. ConnectorId = _request.connectorId,
  240. ErrorCode = _request.errorCode,
  241. Info = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
  242. OCcuredOn = _request.timestamp ?? DateTime.UtcNow,
  243. VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
  244. });
  245. }
  246. var confirm = new StatusNotificationConfirmation() { };
  247. result.Message = confirm;
  248. result.Success = true;
  249. }
  250. break;
  251. case Actions.Heartbeat:
  252. {
  253. var confirm = new HeartbeatConfirmation() { currentTime = DateTime.UtcNow };
  254. result.Message = confirm;
  255. result.Success = true;
  256. }
  257. break;
  258. case Actions.MeterValues:
  259. {
  260. MeterValuesRequest _request = request as MeterValuesRequest;
  261. if (_request.meterValue.Count > 0)
  262. {
  263. using (var db = new MeterValueDBContext())
  264. {
  265. foreach (var item in _request.meterValue)
  266. {
  267. if (_request.transactionId.HasValue)
  268. {
  269. decimal meterStart = 0;
  270. var energy_Register = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Register).FirstOrDefault();
  271. if (energy_Register != null)
  272. {
  273. decimal energyRegister = decimal.Parse(energy_Register.value);
  274. energyRegister = energy_Register.unit.Value == UnitOfMeasure.kWh ? decimal.Multiply(energyRegister, 1000) : energyRegister;
  275. using (var maindb = new MainDBContext())
  276. {
  277. meterStart = maindb.TransactionRecord.Where(x => x.Id == _request.transactionId.Value).Select(x => x.MeterStart).FirstOrDefault();
  278. }
  279. item.sampledValue.Add(new SampledValue()
  280. {
  281. context = ReadingContext.Sample_Periodic,
  282. format = ValueFormat.Raw,
  283. location = Location.Outlet,
  284. phase = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Register).Select(x => x.phase).FirstOrDefault(),
  285. unit = UnitOfMeasure.Wh,
  286. measurand = Measurand.TotalEnergy,
  287. value = decimal.Subtract(energyRegister, meterStart).ToString()
  288. });
  289. }
  290. }
  291. foreach (var sampleVaule in item.sampledValue)
  292. {
  293. decimal value = Convert.ToDecimal(sampleVaule.value);
  294. string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
  295. "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
  296. List<SqlParameter> parameter = new List<SqlParameter>
  297. {
  298. new SqlParameter("ChargeBoxId",session.ChargeBoxId),
  299. new SqlParameter("ConnectorId", (byte)_request.connectorId),
  300. new SqlParameter("Value",value),
  301. new SqlParameter("CreatedOn",item.timestamp),
  302. new SqlParameter("ContextId",sampleVaule.context.HasValue ? (int)sampleVaule.context : 0),
  303. new SqlParameter("FormatId",sampleVaule.format.HasValue ? (int)sampleVaule.format : 0),
  304. new SqlParameter("MeasurandId",sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0),
  305. new SqlParameter("PhaseId",sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0),
  306. new SqlParameter("LocationId",sampleVaule.location.HasValue ? (int)sampleVaule.location : 0),
  307. new SqlParameter("UnitId",sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0),
  308. new SqlParameter("TransactionId",_request.transactionId.HasValue?_request.transactionId:-1),
  309. };
  310. db.Database.ExecuteSqlCommand(sp, parameter.ToArray());
  311. }
  312. }
  313. }
  314. }
  315. // if (energy_kwh > 0)
  316. {
  317. try
  318. {
  319. if (session.IsBilling)
  320. if (session.IsBilling)
  321. {
  322. using (var db = new MainDBContext())
  323. {
  324. db.ServerMessage.Add(new ServerMessage()
  325. {
  326. ChargeBoxId = session.ChargeBoxId,
  327. CreatedBy = "Server",
  328. CreatedOn = DateTime.UtcNow,
  329. OutAction = Actions.DataTransfer.ToString(),
  330. OutRequest = JsonConvert.SerializeObject(
  331. new DataTransferRequest()
  332. {
  333. messageId = "ID_TxEnergy",
  334. vendorId = "Phihong Technology",
  335. data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = _request.connectorId })
  336. },
  337. new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
  338. SerialNo = Guid.NewGuid().ToString(),
  339. InMessage = string.Empty
  340. });
  341. db.SaveChanges();
  342. }
  343. }
  344. }
  345. catch (Exception ex)
  346. {
  347. Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " RunningCost", ex.Message));
  348. }
  349. }
  350. var confirm = new MeterValuesConfirmation() { };
  351. result.Message = confirm;
  352. result.Success = true;
  353. }
  354. break;
  355. case Actions.StartTransaction:
  356. {
  357. StartTransactionRequest _request = request as StartTransactionRequest;
  358. int _transactionId = -1;
  359. var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
  360. var _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
  361. if (_request.idTag != "Backend")
  362. {
  363. var authorization_result = await businessService.Authorize(session.ChargeBoxId, _request.idTag);
  364. _idTagInfo = authorization_result.IdTagInfo;
  365. if (_idTagInfo.status == AuthorizationStatus.Accepted && authorization_result.ChargePointFee != null)
  366. {
  367. var price = authorization_result.ChargePointFee.Where(x => x.IsAC == session.IsAC).First();
  368. if (price != null)
  369. {
  370. if (session.UserPrices.ContainsKey(_request.idTag))
  371. {
  372. session.UserPrices[_request.idTag] = price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString();
  373. session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;
  374. }
  375. else
  376. {
  377. session.UserPrices.Add(_request.idTag, price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString());
  378. session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;
  379. }
  380. }
  381. }
  382. }
  383. //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
  384. if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && _idTagInfo.status == AuthorizationStatus.ConcurrentTx)
  385. {
  386. _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
  387. }
  388. string accountBalance = "0";
  389. if (session.CustomerId.ToString().ToUpper() == "10C7F5BD-C89A-4E2A-8611-B617E0B41A73")
  390. {
  391. using (SqlConnection conn = new SqlConnection(webConnectionString))
  392. {
  393. var parameters = new DynamicParameters();
  394. parameters.Add("@IdTag", _request.idTag, DbType.String, ParameterDirection.Input);
  395. string strSql = "select parentIdTag from [dbo].[LocalListDetail] where ListId = 27 and IdTag=@IdTag; ";
  396. accountBalance = conn.ExecuteScalar<string>(strSql, parameters);
  397. }
  398. }
  399. using (var db = new MainDBContext())
  400. {
  401. db.Database.Log = Console.Write;
  402. var _CustomerId = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).Include(x => x.Customer).
  403. Select(x => x.CustomerId).FirstOrDefault();
  404. var _existedTx = db.TransactionRecord.Where(x => x.CustomerId == _CustomerId && x.ChargeBoxId == session.ChargeBoxId
  405. && x.ConnectorId == _request.connectorId && x.StartTime == _request.timestamp).Select(C => new { C.Id }).AsNoTracking().FirstOrDefault();
  406. TransactionRecord _newTransaction = new TransactionRecord();
  407. if (_existedTx == null)
  408. {
  409. _newTransaction = new TransactionRecord()
  410. {
  411. ChargeBoxId = session.ChargeBoxId,
  412. ConnectorId = (byte)_request.connectorId,
  413. CreatedOn = DateTime.UtcNow,
  414. StartIdTag = _request.idTag,
  415. MeterStart = _request.meterStart,
  416. CustomerId = _CustomerId,
  417. StartTime = _request.timestamp.ToUniversalTime(),
  418. ReservationId = _request.reservationId.HasValue ? _request.reservationId.Value : 0,
  419. };
  420. if (session.UserPrices.ContainsKey(_request.idTag))
  421. {
  422. _newTransaction.Fee = !session.IsBilling ? string.Empty : session.UserPrices[_request.idTag];
  423. }
  424. else
  425. {
  426. _newTransaction.Fee = !session.IsBilling ? string.Empty : session.BillingMethod == 1 ? JsonConvert.SerializeObject(session.ChargingPrices) : session.ChargingFeebyHour.ToString();
  427. _newTransaction.Fee += !session.IsBilling ? string.Empty : "|+" + accountBalance + "+" + "&" + session.ParkingFee + "&|" + session.Currency;
  428. }
  429. db.TransactionRecord.Add(_newTransaction);
  430. db.SaveChanges();
  431. _transactionId = _newTransaction.Id;
  432. logger.Info("***************************************************** ");
  433. logger.Info(string.Format("{0} :TransactionId {1} ", session.ChargeBoxId, _newTransaction.Id));
  434. logger.Info("***************************************************** ");
  435. }
  436. else
  437. {
  438. _transactionId = _existedTx.Id;
  439. logger.Error("Duplication ***************************************************** " + _existedTx.Id);
  440. }
  441. }
  442. var confirm = new StartTransactionConfirmation()
  443. {
  444. idTagInfo = _idTagInfo,
  445. transactionId = _transactionId
  446. };
  447. result.Message = confirm;
  448. result.Success = true;
  449. }
  450. break;
  451. case Actions.StopTransaction:
  452. {
  453. StopTransactionRequest _request = request as StopTransactionRequest;
  454. int _ConnectorId = 0;
  455. var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
  456. var _idTagInfo = string.IsNullOrEmpty(_request.idTag) ? null : (_request.idTag == "Backend" ?
  457. new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted } : (await businessService.Authorize(session.ChargeBoxId, _request.idTag)).IdTagInfo);
  458. //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
  459. if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && _idTagInfo != null && _idTagInfo.status == AuthorizationStatus.ConcurrentTx)
  460. {
  461. _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
  462. }
  463. try
  464. {
  465. using (var db = new MainDBContext())
  466. {
  467. var transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
  468. && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
  469. //遠傳太久以前的停止充電 直接拒絕 避免電樁持續重送~~~~~~~
  470. if (_request.timestamp < new DateTime(2021, 11, 1))
  471. {
  472. var confirm = new StopTransactionConfirmation()
  473. {
  474. idTagInfo = new IdTagInfo()
  475. {
  476. status = AuthorizationStatus.Invalid
  477. }
  478. };
  479. result.Message = confirm;
  480. result.Success = true;
  481. return result;
  482. }
  483. if (transaction != null)
  484. {
  485. var confirm = new StopTransactionConfirmation()
  486. {
  487. idTagInfo = _idTagInfo
  488. };
  489. //Avoid rewrite transaction data
  490. if (transaction.StopTime != GlobalConfig.DefaultNullTime)
  491. {
  492. result.Message = confirm;
  493. result.Success = true;
  494. return result;
  495. }
  496. _ConnectorId = transaction.ConnectorId;
  497. transaction.MeterStop = _request.meterStop;
  498. transaction.StopTime = _request.timestamp.ToUniversalTime();
  499. transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
  500. transaction.StopReason = _request.reason.HasValue ? _request.reason.Value.ToString() : Reason.Local.ToString();
  501. transaction.StopIdTag = _request.idTag;
  502. transaction.Receipt = string.Empty;
  503. transaction.Cost = session.IsBilling ? -1 : 0;
  504. if (_request.transactionData != null && _request.transactionData.Count > 0)
  505. {
  506. _request.transactionData[0].sampledValue.Add(new SampledValue()
  507. {
  508. context = ReadingContext.Transaction_End,
  509. format = ValueFormat.Raw,
  510. location = Location.Outlet,
  511. phase = _request.transactionData[0].sampledValue.Where(x => x.context.HasValue).Select(x => x.phase).FirstOrDefault(),
  512. unit = UnitOfMeasure.Wh,
  513. measurand = Measurand.TotalEnergy,
  514. value = decimal.Subtract(transaction.MeterStop, transaction.MeterStart).ToString()
  515. });
  516. }
  517. await db.SaveChangesAsync();
  518. if (session.IsBilling)
  519. {
  520. db.ServerMessage.Add(new ServerMessage()
  521. {
  522. ChargeBoxId = session.ChargeBoxId,
  523. CreatedBy = "Server",
  524. CreatedOn = DateTime.UtcNow,
  525. OutAction = Actions.DataTransfer.ToString(),
  526. OutRequest = JsonConvert.SerializeObject(
  527. new DataTransferRequest()
  528. {
  529. messageId = "ID_TxEnergy",
  530. vendorId = "Phihong Technology",
  531. data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
  532. },
  533. new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
  534. SerialNo = Guid.NewGuid().ToString(),
  535. InMessage = string.Empty
  536. });
  537. db.SaveChanges();
  538. }
  539. result.Message = confirm;
  540. result.Success = true;
  541. }
  542. else
  543. {
  544. result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
  545. }
  546. }
  547. #region Save MeterValue
  548. if (_request.transactionData != null)
  549. {
  550. if (_request.transactionData.Count > 0)
  551. {
  552. using (var _meterDb = new MeterValueDBContext())
  553. {
  554. foreach (var item in _request.transactionData)
  555. {
  556. foreach (var sampleVaule in item.sampledValue)
  557. {
  558. decimal value = Convert.ToDecimal(sampleVaule.value);
  559. string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
  560. "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
  561. List<SqlParameter> parameter = new List<SqlParameter>
  562. {
  563. new SqlParameter("ChargeBoxId",session.ChargeBoxId),
  564. new SqlParameter("ConnectorId", (byte)_ConnectorId),
  565. new SqlParameter("Value",value),
  566. new SqlParameter("CreatedOn",item.timestamp),
  567. new SqlParameter("ContextId",sampleVaule.context.HasValue ? (int)sampleVaule.context : 0),
  568. new SqlParameter("FormatId",sampleVaule.format.HasValue ? (int)sampleVaule.format : 0),
  569. new SqlParameter("MeasurandId",sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0),
  570. new SqlParameter("PhaseId",sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0),
  571. new SqlParameter("LocationId",sampleVaule.location.HasValue ? (int)sampleVaule.location : 0),
  572. new SqlParameter("UnitId",sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0),
  573. new SqlParameter("TransactionId",_request.transactionId),
  574. };
  575. _meterDb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
  576. }
  577. }
  578. }
  579. }
  580. }
  581. #endregion
  582. }
  583. catch (Exception ex)
  584. {
  585. result.Exception = new Exception("TransactionId " + _request.transactionId + " " + ex.Message);
  586. result.CallErrorMsg = "Reject Response Message";
  587. result.Success = false;
  588. // return result;
  589. }
  590. }
  591. break;
  592. case Actions.Authorize:
  593. {
  594. AuthorizeRequest _request = request as AuthorizeRequest;
  595. var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
  596. var confirm = new AuthorizeConfirmation()
  597. {
  598. idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted }
  599. };
  600. if (_request.idTag != "Backend")
  601. {
  602. var authorization_result = await businessService.Authorize(session.ChargeBoxId, _request.idTag);
  603. confirm.idTagInfo = authorization_result.IdTagInfo;
  604. if (confirm.idTagInfo.status == AuthorizationStatus.Accepted && authorization_result.ChargePointFee != null)
  605. {
  606. var price = authorization_result.ChargePointFee.Where(x => x.IsAC == session.IsAC).First();
  607. if (price != null)
  608. {
  609. if (session.UserPrices.ContainsKey(_request.idTag))
  610. {
  611. session.UserPrices[_request.idTag] = price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString();
  612. session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;
  613. }
  614. else
  615. {
  616. session.UserPrices.Add(_request.idTag, price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString());
  617. session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;
  618. }
  619. if (session.UserDisplayPrices.ContainsKey(_request.idTag))
  620. {
  621. session.UserDisplayPrices[_request.idTag] = price.DisplayMessage;
  622. }
  623. else
  624. {
  625. session.UserDisplayPrices.Add(_request.idTag, price.DisplayMessage);
  626. }
  627. }
  628. }
  629. }
  630. //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
  631. if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && confirm.idTagInfo.status == AuthorizationStatus.ConcurrentTx)
  632. {
  633. confirm.idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
  634. }
  635. result.Message = confirm;
  636. result.Success = true;
  637. }
  638. break;
  639. default:
  640. {
  641. Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreRequest)", request.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
  642. }
  643. break;
  644. }
  645. }
  646. catch (Exception ex)
  647. {
  648. logger.Fatal(string.Format("chargeBoxId:{0} {1}", session.ChargeBoxId, action));
  649. logger.Fatal(string.Format("Data {0}", request.ToString()));
  650. logger.Fatal(string.Format("Error {0}", ex.ToString()));
  651. result.Exception = ex;
  652. }
  653. return result;
  654. }
  655. async internal Task<MessageResult> ExecuteCoreConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
  656. {
  657. MessageResult result = new MessageResult() { Success = true };
  658. try
  659. {
  660. switch (action)
  661. {
  662. case Actions.DataTransfer:
  663. {
  664. DataTransferConfirmation _confirm = confirm as DataTransferConfirmation;
  665. DataTransferRequest _request = _confirm.GetRequest() as DataTransferRequest;
  666. using (var db = new MainDBContext())
  667. {
  668. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  669. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  670. if (operation != null)
  671. {
  672. operation.FinishedOn = DateTime.UtcNow;
  673. operation.Status = 1;//電樁有回覆
  674. operation.EVSE_Status = (int)_confirm.status;
  675. operation.EVSE_Value = string.IsNullOrEmpty(_confirm.data) ? "" : _confirm.data;
  676. await db.SaveChangesAsync();
  677. }
  678. if (_request.messageId == "ID_FirmwareVersion")
  679. {
  680. var machine = new Machine() { Id = session.MachineId };
  681. if (machine != null)
  682. {
  683. db.Configuration.AutoDetectChangesEnabled = false;
  684. db.Configuration.ValidateOnSaveEnabled = false;
  685. db.Machine.Attach(machine);
  686. machine.BoardVersions = _confirm.data;
  687. db.Entry(machine).Property(x => x.BoardVersions).IsModified = true;
  688. await db.SaveChangesAsync();
  689. }
  690. }
  691. if (_request.messageId == "ID_TxEnergy") //計費
  692. {
  693. if (_confirm.status == DataTransferStatus.Accepted)
  694. {
  695. decimal couponPoint = 0m;
  696. string farewellMessage = string.Empty;
  697. string receipt = string.Empty;
  698. List<ChargingBill> bill = new List<ChargingBill>();
  699. List<ChargingPrice> chargingPrices = new List<ChargingPrice>();
  700. var txEnergy = JsonConvert.DeserializeObject<TransactionEnergy>(_confirm.data);
  701. var feedto = db.TransactionRecord.Where(x => x.Id == txEnergy.TxId).Select(x => new { Id = x.Id, ConnectorId = x.ConnectorId, Fee = x.Fee, StopTime = x.StopTime, StartTime = x.StartTime }).FirstOrDefault();
  702. decimal chargedEnergy = 0m;
  703. if (feedto == null || string.IsNullOrEmpty(feedto.Fee)) return result;
  704. string currency = feedto.Fee.Substring(feedto.Fee.Length - 3);
  705. decimal chargingCost = 0;
  706. if (feedto.Fee.Length > 58)
  707. {
  708. chargingPrices = JsonConvert.DeserializeObject<List<ChargingPrice>>(feedto.Fee.Split('|')[0]);
  709. foreach (var item in txEnergy.PeriodEnergy)
  710. {
  711. DateTime dt = new DateTime(2021, 01, 01, int.Parse(item.Key), 0, 0, DateTimeKind.Utc);
  712. string startTime = dt.ToString("hh:mm tt", new CultureInfo("en-us"));
  713. decimal perfee = 0;
  714. //小數點無條件捨去到第4位
  715. var periodEnergy = (decimal)((int)Decimal.Multiply(item.Value, 10000) / (double)10000);
  716. chargedEnergy += periodEnergy;
  717. if (chargingPrices.Count == 1)
  718. {
  719. perfee = Decimal.Multiply(periodEnergy, chargingPrices[0].Fee);
  720. if (bill.Count == 0)
  721. {
  722. bill.Add(new ChargingBill()
  723. {
  724. StartTime = "12:00 AM",
  725. EndTime = "11:59 PM",
  726. Fee = chargingPrices[0].Fee
  727. });
  728. }
  729. bill[0].PeriodEnergy += periodEnergy;
  730. }
  731. else
  732. {
  733. var price = chargingPrices.Where(x => x.StartTime == startTime).FirstOrDefault();
  734. perfee = Decimal.Multiply(periodEnergy, price.Fee);
  735. bill.Add(new ChargingBill()
  736. {
  737. StartTime = price.StartTime,
  738. EndTime = price.EndTime,
  739. PeriodEnergy = periodEnergy,
  740. Fee = price.Fee,
  741. });
  742. }
  743. if (bill.Count > 0)
  744. {
  745. bill[bill.Count - 1].Total += DollarRounding(perfee, session.Currency);
  746. chargingCost += bill[bill.Count - 1].Total;
  747. if (bill.Count == 1)
  748. {
  749. bill[bill.Count - 1].Total = DollarRounding(Decimal.Multiply(bill[0].PeriodEnergy, bill[0].Fee), session.Currency);
  750. chargingCost = bill[bill.Count - 1].Total;
  751. }
  752. }
  753. }
  754. }
  755. else
  756. {
  757. //以小時計費
  758. foreach (var item in txEnergy.PeriodEnergy)
  759. { //小數點無條件捨去到第4位
  760. var periodEnergy = (decimal)((int)Decimal.Multiply(item.Value, 10000) / (double)10000);
  761. chargedEnergy += periodEnergy;
  762. }
  763. var fee = decimal.Parse(feedto.Fee.Split('|')[0]);
  764. var charging_stoptime = feedto.StopTime == GlobalConfig.DefaultNullTime ? DateTime.Parse(DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm")) : DateTime.Parse(feedto.StopTime.ToString("yyyy/MM/dd HH:mm"));
  765. var charging_starttime = DateTime.Parse(feedto.StartTime.ToString("yyyy/MM/dd HH:mm"));
  766. chargingCost = Decimal.Multiply((decimal)charging_stoptime.Subtract(charging_starttime).TotalHours, fee);
  767. chargingCost = DollarRounding(chargingCost, session.Currency);
  768. }
  769. // 計算停車費
  770. var parkingFee = decimal.Parse(feedto.Fee.Split('&')[1]);
  771. var stoptime = feedto.StopTime == GlobalConfig.DefaultNullTime ? DateTime.Parse(DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm")) : DateTime.Parse(feedto.StopTime.ToString("yyyy/MM/dd HH:mm"));
  772. var starttime = DateTime.Parse(feedto.StartTime.ToString("yyyy/MM/dd HH:mm"));
  773. var totalHours = stoptime.Subtract(starttime).TotalHours;
  774. var parkingCost = Decimal.Multiply((decimal)totalHours, parkingFee);
  775. parkingCost = DollarRounding(parkingCost, session.Currency);
  776. if (feedto.StopTime != GlobalConfig.DefaultNullTime)
  777. {
  778. var customerInfo = db.Customer.Where(x => x.Id == session.CustomerId).Select(x => new { x.InstantStopTxReport, x.ApiUrl, x.ApiKey }).FirstOrDefault();
  779. decimal accountBalance = 0;
  780. decimal.TryParse(feedto.Fee.Split('+')[1], out accountBalance);
  781. var tx = db.TransactionRecord.Where(x => x.Id == txEnergy.TxId).FirstOrDefault();
  782. if (tx == null)
  783. {
  784. Console.WriteLine("Tx is empty");
  785. return result;
  786. }
  787. if (tx.BillingDone) return result;
  788. var startTime = new DateTime(tx.StartTime.Year, tx.StartTime.Month, tx.StartTime.Day, tx.StartTime.Hour, 0, 0);
  789. List<ChargingBill> confirmbill = new List<ChargingBill>();
  790. receipt = string.Format("({0} )Energy:", chargedEnergy);
  791. while (startTime < tx.StopTime)
  792. {
  793. if (bill.Count == 1)
  794. {
  795. confirmbill = bill;
  796. receipt += string.Format("| {0} - {1}:| {2} kWh @ ${3}/kWh=${4}", tx.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")), tx.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")),
  797. confirmbill[0].PeriodEnergy.ToString("0.0000"), bill[0].Fee, bill[0].Total);
  798. break;
  799. }
  800. if (bill.Count == 0)
  801. {
  802. receipt += string.Format("| {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
  803. feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), feedto.Fee.Split('|')[0], chargingCost);
  804. break;
  805. }
  806. if (bill.Count > 1)
  807. {
  808. var time = startTime.ToString("hh:mm tt", new CultureInfo("en-us"));
  809. var tt = bill.Where(x => x.StartTime == time).FirstOrDefault();
  810. confirmbill.Add(tt);
  811. if (confirmbill.Count == 1)
  812. {
  813. confirmbill[0].StartTime = tx.StartTime.ToString("hh:mm tt", new CultureInfo("en-us"));
  814. }
  815. var stopTimeText = tx.StopTime.ToString("hh:mm tt", new CultureInfo("en-us"));
  816. if (confirmbill[confirmbill.Count - 1].StartTime.Contains(stopTimeText.Split(' ')[1]))
  817. {
  818. var subHourText = (int.Parse(stopTimeText.Split(':')[0])).ToString();
  819. subHourText = subHourText.Length == 1 ? "0" + subHourText : subHourText;
  820. if (confirmbill[confirmbill.Count - 1].StartTime.Contains(subHourText))
  821. {
  822. confirmbill[confirmbill.Count - 1].EndTime = stopTimeText;
  823. }
  824. }
  825. receipt += string.Format("| {0} - {1}:| {2} kWh @ ${3}/kWh=${4}", confirmbill[confirmbill.Count - 1].StartTime, confirmbill[confirmbill.Count - 1].EndTime,
  826. confirmbill[confirmbill.Count - 1].PeriodEnergy.ToString("0.0000"), confirmbill[confirmbill.Count - 1].Fee, confirmbill[confirmbill.Count - 1].Total);
  827. if (confirmbill.Count == 24) break;
  828. }
  829. startTime = startTime.AddHours(1);
  830. }
  831. chargingCost = confirmbill.Count > 0 ? confirmbill.Sum(x => x.Total) : chargingCost;
  832. receipt += string.Format("|Total Energy Fee : ${0}", chargingCost);
  833. receipt += string.Format("|Parking Fee: | {0} - {1}: | {2} @ ${3}/hr= ${4}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
  834. feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), (totalHours / 1 >= 1) ? string.Format("{0} hours {1} minutes", (int)totalHours / 1, ((totalHours % 1) * 60).ToString("0.0")) : string.Format("{0} minutes", ((totalHours % 1) * 60).ToString("0.0")), parkingFee, parkingCost);
  835. receipt += string.Format("|Stop Reason: {0}", tx.StopReason);
  836. tx.Cost = chargingCost + parkingCost;
  837. if (customerInfo != null && customerInfo.InstantStopTxReport)
  838. {
  839. var request = new
  840. {
  841. ChargeBoxId = tx.ChargeBoxId,
  842. ConnectorId = tx.ConnectorId,
  843. SessionId = tx.Id,
  844. MeterStart = tx.MeterStart,
  845. MeterStop = tx.MeterStop,
  846. IdTag = tx.StartIdTag,
  847. StartTime = tx.StartTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
  848. StopTime = tx.StopTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
  849. StopReason = tx.StopReasonId < 1 ? "Unknown" : (tx.StopReasonId > 12 ? "Unknown" : ((Reason)tx.StopReasonId).ToString()),
  850. Receipt = tx.Receipt,
  851. TotalCost = tx.Cost,
  852. Fee = tx.Fee
  853. };
  854. logger.Debug("completed_session " + JsonConvert.SerializeObject(request));
  855. var response = await httpClient.Post(customerInfo.ApiUrl + "completed_session", new Dictionary<string, string>()
  856. {
  857. { "PartnerId",session.CustomerId.ToString()}
  858. }, request, customerInfo.ApiKey);
  859. var _httpResult = JsonConvert.DeserializeObject<CPOOuterResponse>(response.Response);
  860. logger.Debug("completed_session Response" + response.Response);
  861. JObject jo = JObject.Parse(_httpResult.Data);
  862. if (jo.ContainsKey("CouponPoint"))
  863. {
  864. couponPoint = jo["CouponPoint"].Value<Decimal>();
  865. }
  866. if (jo.ContainsKey("FarewellMessage"))
  867. {
  868. farewellMessage = jo["FarewellMessage"].Value<string>();
  869. }
  870. }
  871. tx.Receipt = receipt;
  872. tx.BillingDone = true;
  873. db.Configuration.AutoDetectChangesEnabled = false;
  874. db.Configuration.ValidateOnSaveEnabled = false;
  875. db.TransactionRecord.Attach(tx);
  876. db.Entry(tx).Property(x => x.Cost).IsModified = true;
  877. db.Entry(tx).Property(x => x.Receipt).IsModified = true;
  878. db.Entry(tx).Property(x => x.BillingDone).IsModified = true;
  879. db.ServerMessage.Add(new ServerMessage()
  880. {
  881. ChargeBoxId = session.ChargeBoxId,
  882. CreatedBy = "Server",
  883. CreatedOn = DateTime.UtcNow,
  884. OutAction = Actions.DataTransfer.ToString(),
  885. OutRequest = JsonConvert.SerializeObject(
  886. new DataTransferRequest()
  887. {
  888. messageId = "FinalCost",
  889. vendorId = "Phihong Technology",
  890. data = JsonConvert.SerializeObject(new
  891. {
  892. txId = txEnergy.TxId,
  893. description = JsonConvert.SerializeObject(new
  894. {
  895. chargedEnergy = chargedEnergy,
  896. chargingFee = chargingCost,
  897. parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
  898. parkingFee = parkingCost,
  899. currency = currency,
  900. couponPoint = couponPoint,
  901. accountBalance = accountBalance - tx.Cost,
  902. farewellMessage = farewellMessage
  903. })
  904. })
  905. },
  906. new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
  907. SerialNo = Guid.NewGuid().ToString(),
  908. InMessage = string.Empty
  909. }); ;
  910. await db.SaveChangesAsync();
  911. using (var meterdb = new MeterValueDBContext())
  912. {
  913. string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
  914. "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
  915. List<SqlParameter> parameter = new List<SqlParameter>
  916. {
  917. new SqlParameter("ChargeBoxId",session.ChargeBoxId),
  918. new SqlParameter("ConnectorId", (byte)feedto.ConnectorId),
  919. new SqlParameter("Value",chargingCost),
  920. new SqlParameter("CreatedOn",DateTime.UtcNow),
  921. new SqlParameter("ContextId",(int)ReadingContext.Sample_Periodic),
  922. new SqlParameter("FormatId",(int)ValueFormat.Raw),
  923. new SqlParameter("MeasurandId",(int)Measurand.TotalCost),
  924. new SqlParameter("PhaseId", -1),
  925. new SqlParameter("LocationId", -1),
  926. new SqlParameter("UnitId", -1),
  927. new SqlParameter("TransactionId",feedto.Id),
  928. };
  929. meterdb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
  930. }
  931. using (SqlConnection conn = new SqlConnection(webConnectionString))
  932. {
  933. var parameters = new DynamicParameters();
  934. parameters.Add("@IdTag", tx.StartIdTag, DbType.String, ParameterDirection.Input);
  935. parameters.Add("@parentIdTag", accountBalance - tx.Cost, DbType.String, ParameterDirection.Input);
  936. string strSql = "update [dbo].[LocalListDetail] set parentIdTag =@parentIdTag where ListId = 27 and IdTag=@IdTag; ";
  937. await conn.ExecuteAsync(strSql, parameters);
  938. }
  939. #region 提供給PHA 過CDFA認證 使用
  940. if (tx.CustomerId == Guid.Parse("10C7F5BD-C89A-4E2A-8611-B617E0B41A73"))
  941. {
  942. var mail_response = new OuterHttpClient().PostFormDataAsync("http://ocpp.phihong.com.tw/CDFA/" + tx.Id, new Dictionary<string, string>()
  943. {
  944. { "email","2"},
  945. { "to","wonderj@phihongusa.com;jessica_tseng@phihong.com.tw"}
  946. //{ "to","jessica_tseng@phihong.com.tw"}
  947. }, null);
  948. Console.WriteLine(JsonConvert.SerializeObject(mail_response));
  949. }
  950. #endregion
  951. }
  952. else
  953. {
  954. db.ServerMessage.Add(new ServerMessage()
  955. {
  956. ChargeBoxId = session.ChargeBoxId,
  957. CreatedBy = "Server",
  958. CreatedOn = DateTime.UtcNow,
  959. OutAction = Actions.DataTransfer.ToString(),
  960. OutRequest = JsonConvert.SerializeObject(
  961. new DataTransferRequest()
  962. {
  963. messageId = "RunningCost",
  964. vendorId = "Phihong Technology",
  965. data = JsonConvert.SerializeObject(new
  966. {
  967. txId = txEnergy.TxId,
  968. description = JsonConvert.SerializeObject(new
  969. {
  970. chargedEnergy = chargedEnergy,
  971. chargingFee = chargingCost,
  972. parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
  973. parkingFee = parkingCost,
  974. currency = currency
  975. })
  976. })
  977. },
  978. new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
  979. SerialNo = Guid.NewGuid().ToString(),
  980. InMessage = string.Empty
  981. }); ;
  982. await db.SaveChangesAsync();
  983. using (var meterdb = new MeterValueDBContext())
  984. {
  985. string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
  986. "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
  987. List<SqlParameter> parameter = new List<SqlParameter>
  988. {
  989. new SqlParameter("ChargeBoxId",session.ChargeBoxId),
  990. new SqlParameter("ConnectorId", (byte)feedto.ConnectorId),
  991. new SqlParameter("Value",chargingCost),
  992. new SqlParameter("CreatedOn",DateTime.UtcNow),
  993. new SqlParameter("ContextId",(int)ReadingContext.Sample_Periodic),
  994. new SqlParameter("FormatId",(int)ValueFormat.Raw),
  995. new SqlParameter("MeasurandId",(int)Measurand.ChargingCost),
  996. new SqlParameter("PhaseId", -1),
  997. new SqlParameter("LocationId", -1),
  998. new SqlParameter("UnitId", -1),
  999. new SqlParameter("TransactionId",feedto.Id),
  1000. };
  1001. meterdb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
  1002. }
  1003. }
  1004. }
  1005. }
  1006. #region 台泥
  1007. if (_request.messageId == "ID_GetTxUserInfo")
  1008. {
  1009. var txUserInfo = JsonConvert.DeserializeObject<ID_GetTxUserInfo>(_confirm.data);
  1010. if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
  1011. {
  1012. var request = new
  1013. {
  1014. ChargeBoxId = session.ChargeBoxId,
  1015. ConnectorId = txUserInfo.ConnectorId,
  1016. SessionId = txUserInfo.TxId,
  1017. SerialNo = txUserInfo.SerialNo,
  1018. StartTime = txUserInfo.StartTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
  1019. VEMData = txUserInfo.VEMData
  1020. };
  1021. var response = httpClient.Post(GlobalConfig.TCC_API_URL + "start_session", new Dictionary<string, string>()
  1022. {
  1023. { "PartnerId",session.CustomerId.ToString()}
  1024. }, request, GlobalConfig.TCC_SALTKEY);
  1025. logger.Debug(JsonConvert.SerializeObject(response));
  1026. }
  1027. }
  1028. #endregion
  1029. }
  1030. }
  1031. break;
  1032. case Actions.ChangeAvailability:
  1033. {
  1034. ChangeAvailabilityConfirmation _confirm = confirm as ChangeAvailabilityConfirmation;
  1035. ChangeAvailabilityRequest _request = _confirm.GetRequest() as ChangeAvailabilityRequest;
  1036. using (var db = new MainDBContext())
  1037. {
  1038. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1039. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1040. if (operation != null)
  1041. {
  1042. operation.FinishedOn = DateTime.UtcNow;
  1043. operation.Status = 1;//電樁有回覆
  1044. operation.EVSE_Status = (int)_confirm.status;
  1045. operation.EVSE_Value = _confirm.status.ToString();
  1046. await db.SaveChangesAsync();
  1047. }
  1048. }
  1049. }
  1050. break;
  1051. case Actions.ClearCache:
  1052. {
  1053. ClearCacheConfirmation _confirm = confirm as ClearCacheConfirmation;
  1054. ClearCacheRequest _request = _confirm.GetRequest() as ClearCacheRequest;
  1055. using (var db = new MainDBContext())
  1056. {
  1057. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1058. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1059. if (operation != null)
  1060. {
  1061. operation.FinishedOn = DateTime.UtcNow;
  1062. operation.Status = 1;//電樁有回覆
  1063. operation.EVSE_Status = (int)_confirm.status;
  1064. operation.EVSE_Value = _confirm.status.ToString();
  1065. await db.SaveChangesAsync();
  1066. }
  1067. }
  1068. }
  1069. break;
  1070. case Actions.RemoteStartTransaction:
  1071. {
  1072. RemoteStartTransactionConfirmation _confirm = confirm as RemoteStartTransactionConfirmation;
  1073. RemoteStartTransactionRequest _request = _confirm.GetRequest() as RemoteStartTransactionRequest;
  1074. using (var db = new MainDBContext())
  1075. {
  1076. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1077. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1078. if (operation != null)
  1079. {
  1080. operation.FinishedOn = DateTime.UtcNow;
  1081. operation.Status = 1;//電樁有回覆
  1082. operation.EVSE_Status = (int)_confirm.status;
  1083. operation.EVSE_Value = _confirm.status.ToString();
  1084. await db.SaveChangesAsync();
  1085. }
  1086. }
  1087. }
  1088. break;
  1089. case Actions.RemoteStopTransaction:
  1090. {
  1091. RemoteStopTransactionConfirmation _confirm = confirm as RemoteStopTransactionConfirmation;
  1092. RemoteStopTransactionRequest _request = _confirm.GetRequest() as RemoteStopTransactionRequest;
  1093. using (var db = new MainDBContext())
  1094. {
  1095. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1096. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1097. if (operation != null)
  1098. {
  1099. operation.FinishedOn = DateTime.UtcNow;
  1100. operation.Status = 1;//電樁有回覆
  1101. operation.EVSE_Status = (int)_confirm.status;
  1102. operation.EVSE_Value = _confirm.status.ToString();
  1103. await db.SaveChangesAsync();
  1104. }
  1105. }
  1106. }
  1107. break;
  1108. case Actions.Reset:
  1109. {
  1110. ResetConfirmation _confirm = confirm as ResetConfirmation;
  1111. ResetRequest _request = _confirm.GetRequest() as ResetRequest;
  1112. using (var db = new MainDBContext())
  1113. {
  1114. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1115. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1116. if (operation != null)
  1117. {
  1118. operation.FinishedOn = DateTime.UtcNow;
  1119. operation.Status = 1;//電樁有回覆
  1120. operation.EVSE_Status = (int)_confirm.status;
  1121. operation.EVSE_Value = _confirm.status.ToString();
  1122. await db.SaveChangesAsync();
  1123. }
  1124. }
  1125. }
  1126. break;
  1127. case Actions.ChangeConfiguration:
  1128. {
  1129. ChangeConfigurationConfirmation _confirm = confirm as ChangeConfigurationConfirmation;
  1130. ChangeConfigurationRequest _request = _confirm.GetRequest() as ChangeConfigurationRequest;
  1131. using (var db = new MainDBContext())
  1132. {
  1133. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1134. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1135. if (operation != null)
  1136. {
  1137. operation.FinishedOn = DateTime.UtcNow;
  1138. operation.Status = 1;//電樁有回覆
  1139. operation.EVSE_Status = (int)_confirm.status;
  1140. operation.EVSE_Value = _confirm.status.ToString();
  1141. }
  1142. if (_confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.Accepted || _confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.RebootRequired)
  1143. {
  1144. var configure = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();
  1145. var foundConfig = configure.Find(x => x.ConfigureName == _request.key);
  1146. if (foundConfig != null)
  1147. {
  1148. foundConfig.ReadOnly = false;
  1149. foundConfig.ConfigureSetting = _request.value;
  1150. }
  1151. else
  1152. {
  1153. db.MachineConfiguration.Add(new MachineConfiguration()
  1154. {
  1155. ChargeBoxId = session.ChargeBoxId,
  1156. ConfigureName = _request.key,
  1157. ReadOnly = false,
  1158. ConfigureSetting = _request.value
  1159. });
  1160. }
  1161. }
  1162. await db.SaveChangesAsync();
  1163. }
  1164. }
  1165. break;
  1166. case Actions.GetConfiguration:
  1167. {
  1168. try
  1169. {
  1170. GetConfigurationConfirmation _confirm = confirm as GetConfigurationConfirmation;
  1171. // GetConfigurationRequest _request = _confirm.GetRequest() as GetConfigurationRequest;
  1172. using (var db = new MainDBContext())
  1173. {
  1174. var configure = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();
  1175. if (_confirm.configurationKey != null)
  1176. {
  1177. foreach (var item in _confirm.configurationKey)
  1178. {
  1179. string oldValue = string.Empty;
  1180. if (item.key == null)
  1181. {
  1182. Console.WriteLine("*********************");
  1183. }
  1184. var foundConfig = configure.Find(x => x.ConfigureName == item.key);
  1185. if (foundConfig != null)
  1186. {
  1187. if (foundConfig.ConfigureName == null)
  1188. {
  1189. Console.WriteLine("*********************");
  1190. }
  1191. if (foundConfig.ConfigureName == "SecurityProfile")
  1192. {
  1193. oldValue = foundConfig.ConfigureSetting;
  1194. }
  1195. foundConfig.ReadOnly = item.IsReadOnly;
  1196. foundConfig.ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value;
  1197. }
  1198. else
  1199. {
  1200. db.MachineConfiguration.Add(new MachineConfiguration()
  1201. {
  1202. ChargeBoxId = session.ChargeBoxId,
  1203. ConfigureName = item.key,
  1204. ReadOnly = item.IsReadOnly,
  1205. ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value,
  1206. Exists = true
  1207. });
  1208. }
  1209. }
  1210. }
  1211. if (_confirm.unknownKey != null)
  1212. {
  1213. foreach (var item in _confirm.unknownKey)
  1214. {
  1215. var foundConfig = configure.Find(x => x.ConfigureName == item);
  1216. if (foundConfig != null)
  1217. {
  1218. foundConfig.ReadOnly = true;
  1219. foundConfig.ConfigureSetting = string.Empty;
  1220. foundConfig.Exists = false;
  1221. }
  1222. else
  1223. {
  1224. db.MachineConfiguration.Add(new MachineConfiguration()
  1225. {
  1226. ChargeBoxId = session.ChargeBoxId,
  1227. ConfigureName = item
  1228. });
  1229. }
  1230. }
  1231. }
  1232. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1233. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1234. if (operation != null)
  1235. {
  1236. operation.FinishedOn = DateTime.UtcNow;
  1237. operation.Status = 1;//電樁有回覆
  1238. operation.EVSE_Status = 1;
  1239. operation.EVSE_Value = JsonConvert.SerializeObject(_confirm.configurationKey, Formatting.None);
  1240. }
  1241. await db.SaveChangesAsync();
  1242. }
  1243. }
  1244. catch (Exception ex)
  1245. {
  1246. logger.Error(ex.ToString());
  1247. }
  1248. }
  1249. break;
  1250. case Actions.UnlockConnector:
  1251. {
  1252. UnlockConnectorConfirmation _confirm = confirm as UnlockConnectorConfirmation;
  1253. UnlockConnectorRequest _request = _confirm.GetRequest() as UnlockConnectorRequest;
  1254. using (var db = new MainDBContext())
  1255. {
  1256. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1257. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1258. if (operation != null)
  1259. {
  1260. operation.FinishedOn = DateTime.UtcNow;
  1261. operation.Status = 1;//電樁有回覆
  1262. operation.EVSE_Status = (int)_confirm.status;
  1263. operation.EVSE_Value = _confirm.status.ToString();
  1264. await db.SaveChangesAsync();
  1265. }
  1266. }
  1267. }
  1268. break;
  1269. default:
  1270. {
  1271. Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreConfirm)", confirm.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
  1272. }
  1273. break;
  1274. }
  1275. }
  1276. catch (Exception ex)
  1277. {
  1278. logger.Debug("123 " + action + " " + ex.ToString());
  1279. }
  1280. return result;
  1281. }
  1282. internal MessageResult ReceivedCoreError(Actions action, string errorMsg, ClientData session, string requestId)
  1283. {
  1284. MessageResult result = new MessageResult() { Success = true };
  1285. switch (action)
  1286. {
  1287. case Actions.ChangeAvailability:
  1288. case Actions.ChangeConfiguration:
  1289. case Actions.ClearCache:
  1290. case Actions.RemoteStartTransaction:
  1291. case Actions.RemoteStopTransaction:
  1292. case Actions.Reset:
  1293. case Actions.GetConfiguration:
  1294. case Actions.UnlockConnector:
  1295. case Actions.DataTransfer:
  1296. {
  1297. if (action == Actions.DataTransfer)
  1298. {
  1299. logger.Debug(string.Format("DataTransfer Error {0}: {1}", session.ChargeBoxId, requestId));
  1300. }
  1301. using (var db = new MainDBContext())
  1302. {
  1303. var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
  1304. x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
  1305. if (operation != null)
  1306. {
  1307. operation.FinishedOn = DateTime.UtcNow;
  1308. operation.Status = 1;//電樁有回覆
  1309. operation.EVSE_Status = (int)255;//錯誤
  1310. operation.EVSE_Value = errorMsg;
  1311. db.SaveChanges();
  1312. }
  1313. }
  1314. }
  1315. break;
  1316. default:
  1317. {
  1318. Console.WriteLine(string.Format("Not Implement {0} Logic(ReceivedCoreError)", action));
  1319. }
  1320. break;
  1321. }
  1322. return result;
  1323. }
  1324. /// <summary>
  1325. /// 依據幣值處理4捨5入
  1326. /// </summary>
  1327. /// <param name="money"></param>
  1328. /// <param name="currency"></param>
  1329. /// <returns></returns>
  1330. private decimal DollarRounding(decimal money, string currency)
  1331. {
  1332. if (currency == "USD" || currency == "EUR")
  1333. {
  1334. //0.4867
  1335. if ((double)((int)(money * 100) + 0.5) <= (double)(money * 100))
  1336. {
  1337. //money = Decimal.Add(money, (decimal)0.01);//0.4967
  1338. }
  1339. money = Math.Round(money, 2, MidpointRounding.AwayFromZero);
  1340. money = Decimal.Parse(money.ToString("0.00"));
  1341. }
  1342. else
  1343. {
  1344. if ((double)((int)(money) + 0.5) <= (double)money)
  1345. {
  1346. // money = (int) money + 1;
  1347. }
  1348. money = Math.Round(money, 0, MidpointRounding.AwayFromZero);
  1349. money = Decimal.Parse(money.ToString("0"));
  1350. }
  1351. return money;
  1352. }
  1353. }
  1354. }