using Evcb.Domain.Model; using Evcb.Domain.mongo; using Evcb.Repository; using Evcb.Service; using Evcb.Utility; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using OCPP_Packet.Packet.DataTransfer; using OCPPPacket.Packet.DataTransfer; using OCPPPacket.Packet.Feature; using OCPPPacket.Packet.Feature.Core; using OCPPPacket.Packet.Feature.FirmwareManagement; using OCPPPacket.Packet.Feature.LocalAuthListManagement; using OCPPPacket.Packet.Feature.RemoteTrigger; using OCPPPacket.Packet.Feature.Reservation; using OCPPPacket.Packet.Feature.SmartCharging; using OCPPPacket.Packet.Messages; using OCPPPacket.Packet.Messages.Basic; using OCPPPacket.Packet.Messages.Core; using OCPPPacket.Packet.Messages.FirmwareManagement; using OCPPPacket.Packet.Messages.LocalAuthListManagement; using OCPPPacket.Packet.Messages.RemoteTrigger; using OCPPPacket.Packet.Messages.Reservation; using OCPPPacket.Packet.Messages.SmartCharging; using OCPPPacket.Packet.Status; using OCPPServer.Common; using OCPPServer.Protocol; using Packet.Cmd; using System; using System.Collections.Generic; using System.Configuration; using System.Data.Entity; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; namespace OCPPServer.Handler { public class OCPPMessageHandler { private ILogger logger = NLog.LogManager.GetCurrentClassLogger(); private FeatureHandler featureHandler; //private CmdHelper CmdHelper; private List _cmd202lst = new List(); private static readonly Object _clientQueue = new object(); static private MakeConfirmationHandler makeConfirmation = new MakeConfirmationHandler(); static private MakeRequestHandler makeRequest = new MakeRequestHandler(); private string ApiUrl { get; set; } //設定OCPP Message Constant private const int INDEX_MESSAGEID = 0; private const int TYPENUMBER_CALL = 2; private const int TYPENUMBER_CALLRESULT = 3; private const int TYPENUMBER_CALLERROR = 4; private const int INDEX_CALL_ACTION = 2; private const int INDEX_CALL_PAYLOAD = 3; private const int INDEX_CALLRESULT_PAYLOAD = 2; private const int INDEX_CALLERROR_ERRORCODE = 2; private const int INDEX_CALLERROR_DESCRIPTION = 3; private const int INDEX_CALLERROR_PAYLOAD = 4; private const int INDEX_UNIQUEID = 1; /// /// 初始化 OCPPMessageHandler Class /// public OCPPMessageHandler() { this.featureHandler = new FeatureHandler(); ApiUrl = ConfigurationManager.AppSettings.Get("ApiUrl"); #region Core //加入core feature featureHandler.AddFeatureProfile(new AuthorizeFeature()); featureHandler.AddFeatureProfile(new BootNotificationFeature()); featureHandler.AddFeatureProfile(new ChangeAvailabilityFeature()); featureHandler.AddFeatureProfile(new ChangeConfigurationFeature()); featureHandler.AddFeatureProfile(new ClearCacheFeature()); featureHandler.AddFeatureProfile(new DataTransferFeature()); featureHandler.AddFeatureProfile(new GetConfigurationFeature()); featureHandler.AddFeatureProfile(new HeartbeatFeature()); featureHandler.AddFeatureProfile(new StartTransactionFeature()); featureHandler.AddFeatureProfile(new StatusNotificationFeature()); featureHandler.AddFeatureProfile(new MeterValuesFeature()); featureHandler.AddFeatureProfile(new RemoteStartTransactionFeature()); featureHandler.AddFeatureProfile(new RemoteStopTransactionFeature()); featureHandler.AddFeatureProfile(new ResetFeature()); featureHandler.AddFeatureProfile(new StopTransactionFeature()); featureHandler.AddFeatureProfile(new UnlockConnectorFeature()); #endregion Core #region FirmwareManagement //加入FirmwareManagement feature featureHandler.AddFeatureProfile(new GetDiagnosticsFeature()); featureHandler.AddFeatureProfile(new DiagnosticsStatusNotificationFeature()); featureHandler.AddFeatureProfile(new FirmwareStatusNotificationFeature()); featureHandler.AddFeatureProfile(new UpdateFirmwareFeature()); #endregion FirmwareManagement #region LocalAuthListManagement //加入LocalAuthListManagement feature featureHandler.AddFeatureProfile(new GetLocalListVersionFeature()); featureHandler.AddFeatureProfile(new SendLocalListFeature()); #endregion LocalAuthListManagement #region RemoteTrigger //加入RemoteTrigger feature featureHandler.AddFeatureProfile(new TriggerMessageFeature()); #endregion RemoteTrigger #region Reservation //加入Reservation feature featureHandler.AddFeatureProfile(new CancelReservationFeature()); featureHandler.AddFeatureProfile(new ReserveNowFeature()); #endregion Reservation #region SmartCharging //加入SmartCharging feature featureHandler.AddFeatureProfile(new ClearChargingProfileFeature()); featureHandler.AddFeatureProfile(new GetCompositeScheduleFeature()); featureHandler.AddFeatureProfile(new SetChargingProfileFeature()); #endregion SmartCharging } /// /// 解析message type,歸類為Call Message 或 CALLRESULT Message或 CALLERROR Message /// /// OCPP Message /// public BaseMessage Parse(string message) { const int INDEX_MESSAGEID = 0; const int TYPENUMBER_CALL = 2; const int TYPENUMBER_CALLRESULT = 3; const int TYPENUMBER_CALLERROR = 4; const int INDEX_CALL_ACTION = 2; const int INDEX_CALL_PAYLOAD = 3; const int INDEX_CALLRESULT_PAYLOAD = 2; const int INDEX_CALLERROR_ERRORCODE = 2; const int INDEX_CALLERROR_DESCRIPTION = 3; const int INDEX_CALLERROR_PAYLOAD = 4; const int INDEX_UNIQUEID = 1; try { var array = JsonConvert.DeserializeObject(message); BaseMessage msg = null; switch ((int)array[INDEX_MESSAGEID]) { case TYPENUMBER_CALL: { CallMessage call = new CallMessage(); call.action = array[INDEX_CALL_ACTION].ToString(); call.payload = array[INDEX_CALL_PAYLOAD].ToString().Replace("\r\n", ""); msg = call; } break; case TYPENUMBER_CALLRESULT: { CallResultMessage callResult = new CallResultMessage(); callResult.payload = array[INDEX_CALLRESULT_PAYLOAD].ToString().Replace("\r\n", ""); msg = callResult; } break; case TYPENUMBER_CALLERROR: { CallErrorMessage callError = new CallErrorMessage(); callError.ErrorCode = array[INDEX_CALLERROR_ERRORCODE].ToString(); callError.ErrorDescription = array[INDEX_CALLERROR_DESCRIPTION].ToString(); //假如沒有error details ,判斷CallError Message的payload 是 an empty object \{}. if (array.Count > INDEX_CALLERROR_PAYLOAD) { callError.payload = array[INDEX_CALLERROR_PAYLOAD].ToString().Replace("\r\n", ""); } msg = callError; } break; default: throw new Exception("Message Type notSupported"); } msg.id = array[INDEX_UNIQUEID].ToString(); return msg; } catch (Exception ex) { throw new Exception(string.Format("Error=>Communicator.Parse: {0}", ex.ToString())); } } /// /// 解壓縮Payload為OCPP Message /// /// OCPP Message Payload /// OCPP Message Type /// private object UnpackPayload(object payload, Type type) { var ocppMessage = JsonConvert.DeserializeObject(payload.ToString(), type); return ocppMessage; } /// /// 壓縮OCPP Message為Payload /// /// /// private object PackPayload(object ocppMessage) { return JsonConvert.SerializeObject(ocppMessage); } /// /// 透過OCPP Message 的UniqueId,從client queue 取出之前後台送出的request, 透過取出的request找出相對應的confirmation type /// /// Messgae queue /// OCPP Message UniqueId /// OCPP Request Message /// private Type GetConfirmationType(ref Queue queue, string uniqueId, out IRequest requestObj) { IRequest request = queue.RestoreRequest(uniqueId); if (request == null) { requestObj = null; return null; } Feature feature = this.featureHandler.FindFeatureByRequest(request.GetType()); requestObj = request; return feature.GetConfirmationType(); } //處理OCPP errorCode, 相對應到資料庫ChargingRecordStatus的Warning欄位 //DC: //ConnectorLockFailure: 90000,EVCommunicationError: 90001,GroundFailure: 90002,HighTemperature: 90003, //InternalError: 90004,LocalListConflict: 90005,NoError: 90006,OtherError: 90007,OverCurrentFailure: 90008, //OverVoltage: 90009, PowerMeterFailure: 90010,PowerSwitchFailure: 90011,ReaderFailure: 90012,ResetFailure: 90013, //UnderVoltage: 90014, WeakSignal: 90015 //AC: //ConnectorLockFailure: 110000,EVCommunicationError: 110001,GroundFailure: 110002,HighTemperature: 110003, //InternalError: 110004,LocalListConflict: 110005,NoError: 110006,OtherError: 110007,OverCurrentFailure: 110008, //OverVoltage: 110009, PowerMeterFailure: 110010,PowerSwitchFailure: 110011,ReaderFailure: 110012,ResetFailure: 110013, //UnderVoltage: 110014, WeakSignal: 110015 /// /// 處理 Error Code /// /// /// /// private int ProcessErrorCode(bool typeAC, ChargePointErrorCode errorCode) { int warning = 0; switch (errorCode) { case ChargePointErrorCode.ConnectorLockFailure: if (typeAC == true) warning = 110000; // AC else warning = 90000; // DC break; case ChargePointErrorCode.EVCommunicationError: if (typeAC == true) warning = 110001; // AC else warning = 90001; // DC break; case ChargePointErrorCode.GroundFailure: if (typeAC == true) warning = 110002; // AC else warning = 90002; // DC break; case ChargePointErrorCode.HighTemperature: if (typeAC == true) warning = 110003; // AC else warning = 90003; // DC break; case ChargePointErrorCode.InternalError: if (typeAC == true) warning = 110004; // AC else warning = 90004; // DC break; case ChargePointErrorCode.LocalListConflict: if (typeAC == true) warning = 110005; // AC else warning = 90005; // DC break; case ChargePointErrorCode.NoError: if (typeAC == true) warning = 110006; // AC else warning = 90006; // DC break; case ChargePointErrorCode.OtherError: if (typeAC == true) warning = 110007; // AC else warning = 90007; // DC break; case ChargePointErrorCode.OverCurrentFailure: if (typeAC == true) warning = 110008; // AC else warning = 90008; // DC break; case ChargePointErrorCode.OverVoltage: if (typeAC == true) warning = 110009; // AC else warning = 90009; // DC break; case ChargePointErrorCode.PowerMeterFailure: if (typeAC == true) warning = 110010; // AC else warning = 90010; // DC break; case ChargePointErrorCode.PowerSwitchFailure: if (typeAC == true) warning = 110011; // AC else warning = 90011; // DC break; case ChargePointErrorCode.ReaderFailure: if (typeAC == true) warning = 110012; // AC else warning = 90012; // DC break; case ChargePointErrorCode.ResetFailure: if (typeAC == true) warning = 110013; // AC else warning = 90013; // DC break; case ChargePointErrorCode.UnderVoltage: if (typeAC == true) warning = 110014; // AC else warning = 90014; // DC break; case ChargePointErrorCode.WeakSignal: if (typeAC == true) warning = 110015; // AC else warning = 90015; // DC break; default: break; } return warning; } /// /// 產生 random 20 character alphanumeric strings /// /// private string GenerateRandomString() { var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var random = new Random(); var list = Enumerable.Repeat(0, 20).Select(x => chars[random.Next(chars.Length)]); string finalString = string.Join("", list); return finalString; } /// /// 處理 Server Command /// /// server command /// 電樁樁號 /// Messgae queue /// Configuration Key /// 預約卡號 /// 充電檔案 /// Message Type /// Data Base Entity /// public string HandleServerCommand(BaseCmd cmd, string machineCustomId, ref Queue queue, ref List configurationKey, int reservationId, int chargingProfileId, out string messageType, PhihongDbContext db) { string result = string.Empty; messageType = string.Empty; //設定心跳時間和心跳的timeout次數 if (cmd is Cmd1001) { var c = cmd as Cmd1001; if (c.ParamIndex == 21) { var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); if (machine != null) { IRequest req = MakeRequestHandler.MakeChangeConfigurationRequest("HeartbeatInterval", machine.HeartbeatInterval.ToString()); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "ChangeConfiguration Request(HeartbeatInterval)"; result = GenerateCall(uuid, Actions.ChangeConfiguration.ToString(), req); } } } //校時 if (cmd is Cmd1003) { var c = cmd as Cmd1003; var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); if (machine != null) { //確認發送是校時參數,就修改校時時間為封包送出時間 if (c.ParamIndex == 2) { IRequest req = MakeRequestHandler.MakeChangeConfigurationRequest("HeartbeatInterval", "1"); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "ChangeConfiguration Request(HeartbeatInterval)"; result = GenerateCall(uuid, Actions.ChangeConfiguration.ToString(), req); } //確認發送設定Server Domain Name if (c.ParamIndex == 11) { //OCPP 不支援Server Domain Name messageType = "Server Domain Name Request"; result = String.Empty; } //確認發送設定樁的夥伴代碼 if (c.ParamIndex == 12) { IRequest req = makeRequest.MakeSendPartnerPoleIdRequest(machine.PartnerPoleId); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "DataTransfer Request(SendPartnerPoleId)"; result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } } } //Remote結束充電 if (cmd is Cmd1005) { var c = cmd as Cmd1005; //int transactionId = 0; var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); //確認發送停止充電 if (c.ParamIndex == 2) { if (machine != null) { var gunStatusNow = db.MachineGun.Where(x => x.GunSerNo == c.GunSerNo && x.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); //如果是 DC且狀態等於 2(充電中) 或是 AC或 HMI 且在充電中 if ((machine.MachineModelId == 2 && gunStatusNow.Status == 2) || (machine.MachineModelId != 2 && (gunStatusNow.Status == 2 || gunStatusNow.Status == 3))) { var record = db.ChargingRecord.Where(x => x.GunSerNo == c.GunSerNo && x.MachineId == machine.Id && x.ClientStartChargingDateTime == null && x.ClientEndChargingDateTime == null && x.EndChargingDateTime == null && x.StartChargingDateTime != null && x.ChargingSerNo == gunStatusNow.ChargingSerNo).AsNoTracking().FirstOrDefault(); var chargingTransaction = db.ChargingTransaction.Where(x => x.ChargingRecordId == record.Id).AsNoTracking().FirstOrDefault(); IRequest req = makeRequest.MakeRemoteStopTransactionRequest(chargingTransaction.TransactionId); //client.RemoteCharging = true; /// Remote充電 string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "RemoteStopTransaction Request"; result = GenerateCall(uuid, Actions.RemoteStopTransaction.ToString(), req); } } } //確認發送停止預約 if (c.ParamIndex == 10) { if (machine != null) { IRequest req = makeRequest.MakeCancelReservationRequest(reservationId); //client.RemoteCharging = true; /// Remote充電 string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "CancelReservation Request"; result = GenerateCall(uuid, Actions.CancelReservation.ToString(), req); } } //確認發送Psu Log To Ftp (發送 GetDiagnostics 訊息) if (c.ParamIndex == 17) { if (machine != null) { IRequest req = MakeRequestHandler.MakeGetDiagnosticsRequest(machine.AC); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "GetDiagnostics Request"; result = GenerateCall(uuid, Actions.GetDiagnostics.ToString(), req); } } /// 18: QRCode Flash 效果 , 19: 禁用充电桩, 20: 启用充电桩 if (c.ParamIndex == 18 || c.ParamIndex == 19 || c.ParamIndex == 20) { if (machine != null) { var type = Convert.ToInt32(c.ParamIndex); int gunNo = 0; switch (type) { //QRCode Flash 效果 case 18: gunNo = c.GunSerNo + 1; break; // 19: 禁用充电桩 case 19: gunNo = 0; break; //20: 启用充电桩 case 20: gunNo = 0; break; } IRequest req = makeRequest.MakeCustomCommandRequest(gunNo, type); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "DataTransfer Request(CustomCommand)"; result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } } } //Remote啟動充電 / 預約充電 if (cmd is Cmd1007) { var c = cmd as Cmd1007; if (c.StartChargingType == 0) // 0:实时充电 { var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); var memberQuery = db.MemberCharging.Where(x => x.MemberId == c.ReservationCardNum).AsNoTracking().FirstOrDefault(); string randomString = string.Empty; if (memberQuery == null) // member id不存在,才產生新的SelfDefinedId { randomString = GenerateRandomString(); var selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault(); while (selfDefinedIdQuery != null) { randomString = GenerateRandomString(); selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault(); } //將 random 20 character alphanumeric strings 與 MemberId 儲存到 Member 表格,之後處理Remote充電會透過Member表格取得 MemberId MemberMapping remotemember = new MemberMapping(); remotemember.MemberId = c.ReservationCardNum; remotemember.SelfDefinedId = randomString; remotemember.CustomerId = machine.CustomerId; remotemember.CreatedOn = DateTime.Now; db.MemberCharging.Add(remotemember); db.SaveChanges(); } else { randomString = memberQuery.SelfDefinedId; } //處理Remote充電命令,並且發送給電樁 IRequest req = makeRequest.MakeRemoteStartTransactionRequest((int)c.GunSerNo + 1, randomString); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "RemoteStartTransaction Request"; result = GenerateCall(uuid, Actions.RemoteStartTransaction.ToString(), req); } if (c.StartChargingType == 1) // 1: 定時啟動充電 { var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); var memberQuery = db.MemberCharging.Where(x => x.MemberId == c.ReservationCardNum).AsNoTracking().FirstOrDefault(); string randomString = string.Empty; if (memberQuery == null) // member id不存在,才產生新的SelfDefinedId { randomString = GenerateRandomString(); var selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault(); while (selfDefinedIdQuery != null) { randomString = GenerateRandomString(); selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault(); } //將 random 20 character alphanumeric strings 與 MemberId 儲存到 Member 表格,之後處理Remote充電會透過Member表格取得 MemberId MemberMapping remotemember = new MemberMapping(); remotemember.MemberId = c.ReservationCardNum; remotemember.SelfDefinedId = randomString; remotemember.CustomerId = machine.CustomerId; remotemember.CreatedOn = DateTime.Now; db.MemberCharging.Add(remotemember); db.SaveChanges(); } else { randomString = memberQuery.SelfDefinedId; } AutoChargeReservation autoChargeReservation = new AutoChargeReservation(); autoChargeReservation.connectorId = (int)c.GunSerNo + 1; autoChargeReservation.chargingStrategy = (ChargingStrategy)c.ChargingStrategy; autoChargeReservation.chargingStrategyParam = (double)c.ChargingStrategyParam; autoChargeReservation.startChargingDateTime = (DateTime)c.StartChargingDateTime; autoChargeReservation.idTag = randomString; autoChargeReservation.accountBalance = (double)c.AccountBalance.RealValue; autoChargeReservation.memberName = c.MemberName; autoChargeReservation.vehicleType = (int)c.VehicleType; autoChargeReservation.reservationId = reservationId; //處理定時啟動充電,並且發送給電樁 IRequest req = makeRequest.MakeAutoChargeReservationRequest(autoChargeReservation); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "DataTransfer Request(AutoChargeReservation)"; result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } if (c.StartChargingType == 2) // 2:预约充电 { var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); var memberQuery = db.MemberCharging.Where(x => x.MemberId == c.ReservationCardNum).AsNoTracking().FirstOrDefault(); string randomString = string.Empty; if (memberQuery == null) // member id不存在,才產生新的SelfDefinedId { randomString = GenerateRandomString(); var selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault(); while (selfDefinedIdQuery != null) { randomString = GenerateRandomString(); selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault(); } //將 random 20 character alphanumeric strings 與 MemberId 儲存到 Member 表格,之後處理Remote充電會透過Member表格取得 MemberId MemberMapping remotemember = new MemberMapping(); remotemember.MemberId = c.ReservationCardNum; remotemember.SelfDefinedId = randomString; remotemember.CustomerId = machine.CustomerId; remotemember.CreatedOn = DateTime.Now; db.MemberCharging.Add(remotemember); db.SaveChanges(); } else { randomString = memberQuery.SelfDefinedId; } var item = configurationKey.Where(a => a.key == "ReserveConnectorZeroSupported").FirstOrDefault(); IRequest req = makeRequest.MakeReserveNowRequest((int)c.GunSerNo + 1, randomString, Convert.ToBoolean(item.value), reservationId); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "ReserveNow Request"; result = GenerateCall(uuid, Actions.ReserveNow.ToString(), req); } } //后台服务器下发充电桩本地验证卡号名单命令 if (cmd is Cmd1009) { var c = cmd as Cmd1009; string sLine = ""; if (!Directory.Exists(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadFiles"))) { Directory.CreateDirectory(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadFiles")); } string fileName = System.IO.Path.GetFileName(c.Url); string destFilePath = System.IO.Path.Combine(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadFiles/"), fileName); if (!Directory.Exists(destFilePath)) { List tList = new List(); //downloading using (WebClient webClient = new WebClient()) { webClient.Credentials = CredentialCache.DefaultNetworkCredentials; if (c.Url.Contains("http") == false) { c.Url = "http://" + c.Url; } tList.Add(Task.Run(() => webClient.DownloadFileTaskAsync(new Uri(c.Url), destFilePath))); } Task.WaitAll(tList.ToArray()); } if (File.Exists(destFilePath)) { using (FileStream file = new FileStream(destFilePath, FileMode.Open, FileAccess.Read)) { using (StreamReader srReader = new StreamReader(file)) { sLine = srReader.ReadToEnd(); } } } List localAuthorizationList = JsonConvert.DeserializeObject>(sLine); IRequest req = MakeRequestHandler.MakeSendLocalListRequest(localAuthorizationList, c.VersionNo, c.Type); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "SendLocalListRequest Request"; result = GenerateCall(uuid, Actions.SendLocalList.ToString(), req); } if (cmd is Cmd1011) { IRequest req = MakeRequestHandler.MakeGetLocalListVersionRequest(); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "GetLocalListVersion Request"; result = GenerateCall(uuid, Actions.GetLocalListVersion.ToString(), req); } if (cmd is Cmd1013) { var c = cmd as Cmd1013; int TransactionId = 0; var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); var gunStatusNow = db.MachineGun.Where(y => y.GunSerNo == c.GunSerNo && y.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); //充電目的檔為 TxProfile if (gunStatusNow != null && c.ChargingProfilePurpose == 2) { //確認電樁在充電中 if (gunStatusNow.Status == 2 || gunStatusNow.Status == 3) { var chargingRecord = db.ChargingRecord.Where(x => x.ChargingSerNo == gunStatusNow.ChargingSerNo).AsNoTracking().FirstOrDefault(); if (chargingRecord != null) { var chargingTransaction = db.ChargingTransaction.Where(x => x.MachineId == machine.Id && x.GunSerNo == c.GunSerNo && x.ChargingRecordId == chargingRecord.Id).AsNoTracking().FirstOrDefault(); if (chargingTransaction != null) { TransactionId = chargingTransaction.TransactionId; } } } } IRequest req = MakeRequestHandler.MakeSetChargingProfileRequest(cmd, TransactionId); lock (_clientQueue) { queue.store(req, c.UUID); } messageType = "SetChargingProfile Request"; result = GenerateCall(c.UUID, Actions.SetChargingProfile.ToString(), req); } if (cmd is Cmd1015) { var c = cmd as Cmd1015; IRequest req = makeRequest.MakeClearChargingProfileRequest(cmd); lock (_clientQueue) { queue.store(req, c.UUID); } messageType = "ClearChargingProfile Request"; result = GenerateCall(c.UUID, Actions.ClearChargingProfile.ToString(), req); } if (cmd is Cmd1017) { var c = cmd as Cmd1017; IRequest req = MakeRequestHandler.MakeSendChargingConfigRequest(cmd); lock (_clientQueue) { queue.store(req, c.UUID); } messageType = "SetChargingConfig Request"; result = GenerateCall(c.UUID, Actions.DataTransfer.ToString(), req); } if (cmd is Cmd1019) { var c = cmd as Cmd1019; IRequest req = MakeRequestHandler.MakeSendGetChargingConfigRequest(cmd); lock (_clientQueue) { queue.store(req, c.UUID); } messageType = "GetChargingConfig Request"; result = GenerateCall(c.UUID, Actions.DataTransfer.ToString(), req); } //設定每度費用 if (cmd is Cmd2301) { var c = cmd as Cmd2301; IRequest req = makeRequest.MakeAllDayUniformElectricBillRequest(c.PricePerKWH.RealValue); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "DataTransfer Request(AllDayUniformElectricBill)"; result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } //設定區段費用 if (cmd is Cmd2303) { var c = cmd as Cmd2303; List sectionElectricBill = new List(); sectionElectricBill.Add((double)c.Section0000_0030.RealValue); sectionElectricBill.Add((double)c.Section0030_0100.RealValue); sectionElectricBill.Add((double)c.Section0100_0130.RealValue); sectionElectricBill.Add((double)c.Section0130_0200.RealValue); sectionElectricBill.Add((double)c.Section0200_0230.RealValue); sectionElectricBill.Add((double)c.Section0230_0300.RealValue); sectionElectricBill.Add((double)c.Section0300_0330.RealValue); sectionElectricBill.Add((double)c.Section0330_0400.RealValue); sectionElectricBill.Add((double)c.Section0400_0430.RealValue); sectionElectricBill.Add((double)c.Section0430_0500.RealValue); sectionElectricBill.Add((double)c.Section0500_0530.RealValue); sectionElectricBill.Add((double)c.Section0530_0600.RealValue); sectionElectricBill.Add((double)c.Section0600_0630.RealValue); sectionElectricBill.Add((double)c.Section0630_0700.RealValue); sectionElectricBill.Add((double)c.Section0700_0730.RealValue); sectionElectricBill.Add((double)c.Section0730_0800.RealValue); sectionElectricBill.Add((double)c.Section0800_0830.RealValue); sectionElectricBill.Add((double)c.Section0830_0900.RealValue); sectionElectricBill.Add((double)c.Section0900_0930.RealValue); sectionElectricBill.Add((double)c.Section0930_1000.RealValue); sectionElectricBill.Add((double)c.Section1000_1030.RealValue); sectionElectricBill.Add((double)c.Section1030_1100.RealValue); sectionElectricBill.Add((double)c.Section1100_1130.RealValue); sectionElectricBill.Add((double)c.Section1130_1200.RealValue); sectionElectricBill.Add((double)c.Section1200_1230.RealValue); sectionElectricBill.Add((double)c.Section1230_1300.RealValue); sectionElectricBill.Add((double)c.Section1300_1330.RealValue); sectionElectricBill.Add((double)c.Section1330_1400.RealValue); sectionElectricBill.Add((double)c.Section1400_1430.RealValue); sectionElectricBill.Add((double)c.Section1430_1500.RealValue); sectionElectricBill.Add((double)c.Section1500_1530.RealValue); sectionElectricBill.Add((double)c.Section1530_1600.RealValue); sectionElectricBill.Add((double)c.Section1600_1630.RealValue); sectionElectricBill.Add((double)c.Section1630_1700.RealValue); sectionElectricBill.Add((double)c.Section1700_1730.RealValue); sectionElectricBill.Add((double)c.Section1730_1800.RealValue); sectionElectricBill.Add((double)c.Section1800_1830.RealValue); sectionElectricBill.Add((double)c.Section1830_1900.RealValue); sectionElectricBill.Add((double)c.Section1900_1930.RealValue); sectionElectricBill.Add((double)c.Section1930_2000.RealValue); sectionElectricBill.Add((double)c.Section2000_2030.RealValue); sectionElectricBill.Add((double)c.Section2030_2100.RealValue); sectionElectricBill.Add((double)c.Section2100_2130.RealValue); sectionElectricBill.Add((double)c.Section2130_2200.RealValue); sectionElectricBill.Add((double)c.Section2200_2230.RealValue); sectionElectricBill.Add((double)c.Section2230_2300.RealValue); sectionElectricBill.Add((double)c.Section2300_2330.RealValue); sectionElectricBill.Add((double)c.Section2330_2400.RealValue); IRequest req = makeRequest.MakeAllDaySectionElectricBillRequest(sectionElectricBill); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "DataTransfer Request(AllDaySectionElectricBill)"; result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } //設定每度服務費 if (cmd is Cmd2305) { var c = cmd as Cmd2305; IRequest req = makeRequest.MakeFeePerKWHRequest(c.FeePerKWH.RealValue); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "DataTransfer Request(FeePerKWH)"; result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } //ocpp cmd: MessageTrigger BootNotification if (cmd is Cmd9001) { IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.BootNotification, 1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "TriggerMessage Request(BootNotification)"; result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req); } //ocpp cmd: MessageTrigger DiagnosticsStatusNotification if (cmd is Cmd9003) { IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.DiagnosticsStatusNotification, 1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "TriggerMessage Request(DiagnosticsStatusNotification)"; result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req); } //ocpp cmd: MessageTrigger FirmwareStatusNotification if (cmd is Cmd9005) { IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.FirmwareStatusNotification, 1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "TriggerMessage Request(FirmwareStatusNotification)"; result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req); } //ocpp cmd: MessageTrigger Heartbeat if (cmd is Cmd9007) { IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.Heartbeat, 1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "TriggerMessage Request(Heartbeat)"; result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req); } //ocpp cmd: MessageTrigger MeterValues if (cmd is Cmd9009) { IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.MeterValues, 1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "TriggerMessage Request(MeterValues)"; result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req); } //ocpp cmd: MessageTrigger StatusNotification if (cmd is Cmd9011) { IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.StatusNotification, 1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "TriggerMessage Request(StatusNotification)"; result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req); } //ocpp cmd : SendLocalListRequest , GetLocalListVersion , OCPPUpdateFirmware if (cmd is Cmd9013) { var c = cmd as Cmd9013; //OCPP UpdateFirmware if (c.ParamIndex == 4) { var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); IRequest req = MakeRequestHandler.MakeOCPPUpdateFirmwareRequest(@"http://"); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "OCPPUpdateFirmware Request"; result = GenerateCall(uuid, Actions.UpdateFirmware.ToString(), req); } } //ocpp cmd : SetChargingProfile if (cmd is Cmd9015) { var c = cmd as Cmd9015; string purpose = System.Text.Encoding.ASCII.GetString(c.ParamByteList.ToArray()).Trim('\0'); var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault(); IRequest req = MakeRequestHandler.MakeSetChargingProfileRequest(0, 0, chargingProfileId, purpose); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "SetChargingProfile Request"; result = GenerateCall(uuid, Actions.SetChargingProfile.ToString(), req); if (result.Contains("TxProfile") == false) { int location = result.IndexOf("transactionId"); int len = "\"transactionId\":0,".Length; result = result.Remove(location - 1, len); } } //ocpp cmd : ClearChargingProfile if (cmd is Cmd9017) { IRequest req = makeRequest.MakeClearChargingProfileRequest(0); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "ClearChargingProfile Request"; result = GenerateCall(uuid, Actions.ClearChargingProfile.ToString(), req); } //ocpp cmd : GetCompositeSchedule if (cmd is Cmd9019) { IRequest req = makeRequest.MakeGetCompositeScheduleRequest(0); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "GetCompositeSchedule Request"; result = GenerateCall(uuid, Actions.GetCompositeSchedule.ToString(), req); } //ocpp cmd : HardReset if (cmd is Cmd9021) { IRequest req = makeRequest.MakeHardResetRequest(); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "Reset Request(Hard)"; result = GenerateCall(uuid, Actions.Reset.ToString(), req); } //ocpp cmd : SoftReset if (cmd is Cmd9023) { IRequest req = makeRequest.MakeSoftResetRequest(); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "Reset Request(Soft)"; result = GenerateCall(uuid, Actions.Reset.ToString(), req); } //ocpp cmd : ClearCache if (cmd is Cmd9025) { IRequest req = makeRequest.MakeClearCacheRequest(); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "ClearCache Request"; result = GenerateCall(uuid, Actions.ClearCache.ToString(), req); } //ocpp cmd : ChangeAvailability if (cmd is Cmd9027) { IRequest req = makeRequest.MakeChangeAvailabilityRequest(1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "ChangeAvailability Request"; result = GenerateCall(uuid, Actions.ChangeAvailability.ToString(), req); } //ocpp cmd : GetConfiguration if (cmd is Cmd9029) { IRequest req = makeRequest.MakeGetConfigurationRequest(""); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "GetConfiguration Request"; result = GenerateCall(uuid, Actions.GetConfiguration.ToString(), req); } //ocpp cmd : UnlockConnector if (cmd is Cmd9031) { IRequest req = makeRequest.MakeUnlockConnectorRequest(1); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } messageType = "UnlockConnector Request"; result = GenerateCall(uuid, Actions.UnlockConnector.ToString(), req); } return result; } /// /// 處理OCPP Request Message /// /// OCPP Request Message /// OCPP Request Message Call Id /// Data Base Entity /// Connected Client Data /// public OCPPResult HandleRequest(IRequest request, string callid, PhihongDbContext db, ref ClientData client) { OCPPResult result = new OCPPResult(); IUnitOfWork uowtemp = new UnitOfWork(db); IMachineService machineSrvTemp = new MachineService(uowtemp); ISocketCommandService socketCommandSrvTemp = new SocketCommandService(uowtemp); ICustomerService _customerServiceTemp = new CustomerService(); try { //簽到 if (request is BootNotificationRequest) { var cmd = request as BootNotificationRequest; result.Success = true; result.Payload = makeConfirmation.MakeBootNotificationConfirmation(cmd); } //刷卡認證 if (request is AuthorizeRequest) { #region AuthorizeRequest var cmd = request as AuthorizeRequest; var c = new HttpClient(); string receivedCard = string.Empty; string cardpwd = string.Empty; //判斷此Authorize Reauest是Remote啟動充電 或 刷卡/後台啟動 MemberMapping memberObj = db.MemberCharging.Where(x => x.SelfDefinedId == cmd.idTag).AsNoTracking().FirstOrDefault(); if (memberObj != null)//為Remote啟動充電 { receivedCard = memberObj.MemberId; } else //為刷卡或後台啟動 { receivedCard = cmd.idTag; cardpwd = "nocheck"; } var param = new { CustomerName = client.CustomerName, CardNum = receivedCard, PoleId = client.MachineCustomId, CardPwd = cardpwd }; //將卡片資訊送到營運平台驗證 var response = c.PostAsJsonAsync(ApiUrl + @"AuthenticateCard", param).Result; string responseBody = response.Content.ReadAsStringAsync().Result; if (response.IsSuccessStatusCode) { //伺服器回應正常 var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId); var authresult = JsonConvert.DeserializeObject(responseBody); if (authresult.UserMessageCode == 1) //卡片餘額充足 { //卡片認證成功 result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Accepted); #region 下發卡片餘額 IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); #endregion 下發卡片餘額 } else { if (authresult.UserMessageCode == 6) //卡片被凍結 { //卡片認證失敗,檢查卡片是否在充電中 //取得充電樁目前的Status string rqueryCard = string.Empty; if (memberObj != null) //為Remote啟動充電 { rqueryCard = memberObj.MemberId; } else { rqueryCard = cmd.idTag; } //若電樁狀態不是"充電中"/"充電結束" ,MachineGun的ReservationCardNum欄位為空白,此時取出的 gunStatusNow 為null var gunStatusNow = db.MachineGun.Where(x => x.ReservationCardNum == rqueryCard && x.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); if (gunStatusNow != null && (gunStatusNow.Status == 2 || gunStatusNow.Status == 3)) { //取得充電樁在充電中或充電結束的狀態,回應有效 result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Accepted); } else { //取得充電樁不在充電中或充電結束的狀態,回應無效 result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Blocked); #region 下發卡片餘額 { IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } #endregion 下發卡片餘額 } } else { //卡片為非餘額充足.非凍結的其他狀態 result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Invalid); #region 下發卡片餘額 { IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } #endregion 下發卡片餘額 } } } else { //伺服器異常 result.Payload = makeConfirmation.MakeAuthorizeConfirmation((AuthorizeRequest)request, AuthorizationStatus.Invalid); } result.Success = true; #endregion AuthorizeRequest } //狀態包 if (request is StatusNotificationRequest) { #region StatusNotificationRequest //目前以DataTransfer的GunStatus封包取代 var cmd = request as StatusNotificationRequest; logger.Trace("Receive Command Status{0}", client.MachineCustomId); result.Success = true; result.Payload = makeConfirmation.MakeStatusNotificationConfirmation(cmd); #endregion StatusNotificationRequest } //心跳包 if (request is HeartbeatRequest) { result.Success = true; result.Payload = makeConfirmation.MakeHeartbeatConfirmation((HeartbeatRequest)request); client.heartbeatDate = DateTime.Now; } //充電狀態包 if (request is MeterValuesRequest) { #region MeterValuesRequest var cmd = request as MeterValuesRequest; logger.Trace("Receive Command Status{0}", client.MachineCustomId); WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); //處理104狀態包 //不存在機器,要紀錄起來 var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId); if (machine != null) { //取得充電樁目前Status的充電序號 var gunStatusNow = db.MachineGun.Where(c => c.GunSerNo == (cmd.connectorId - 1) && c.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); if (gunStatusNow.Status != (byte)ChargePointStatus.Charging) { result.Success = true; result.Payload = makeConfirmation.MakeMeterValuesConfirmation(cmd); return result; } result.Success = true; result.Payload = makeConfirmation.MakeMeterValuesConfirmation(cmd); } #endregion MeterValuesRequest } //啟動充電狀態包 if (request is StartTransactionRequest) { #region StartTransactionRequest var cmd = request as StartTransactionRequest; logger.Trace("Receive Command Status{0}", client.MachineCustomId); WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); //處理104狀態包 //不存在機器,要紀錄起來 var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId); if (machine != null) { //取得充電樁目前Status的充電序號 var gunStatusNow = db.MachineGun.Where(c => c.GunSerNo == (cmd.connectorId - 1) && c.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); int transactionId = 0; result.Success = true; #region 確認是否有重覆的StartTransaction var repeatStartTransaction = db.ChargingTransaction.Where(c => c.GunSerNo == (cmd.connectorId - 1) && c.MachineId == machine.Id && c.StartChargingDateTime == cmd.timestamp ).AsNoTracking().FirstOrDefault(); if (repeatStartTransaction != null) //重複的StartTransaction result.Payload = makeConfirmation.MakeRePeatStartTransactionConfirmation(cmd, repeatStartTransaction.TransactionId); else result.Payload = makeConfirmation.MakeStartTransactionConfirmation(cmd, out transactionId); #endregion 確認是否有重覆的StartTransaction //取得OCPP transactionId之後,寫入mapping table(OCPP transactionId, 充電紀錄ID),取得充電樁目前Status的充電序號 if (gunStatusNow.Status != (byte)ChargePointStatus.Charging) { gunStatusNow = db.MachineGun.Where(c => c.GunSerNo == (cmd.connectorId - 1) && c.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); } var record = db.ChargingRecord.Where(c => c.GunSerNo == (cmd.connectorId - 1) && c.MachineId == machine.Id && c.ClientStartChargingDateTime == null && c.ClientEndChargingDateTime == null && c.EndChargingDateTime == null && c.StartChargingDateTime != null && c.ChargingSerNo == gunStatusNow.ChargingSerNo).AsNoTracking().FirstOrDefault(); //處理沒有重複送的StartTransaction封包 if (record != null) { //避免在物件 'dbo.ChargingTransaction' 中插入重複的索引鍵資料列 var chargingTransactiontemp = db.ChargingTransaction.Where(c => c.GunSerNo == (cmd.connectorId - 1) && c.MachineId == machine.Id && c.StartChargingDateTime == record.StartChargingDateTime ).AsNoTracking().FirstOrDefault(); if (chargingTransactiontemp == null) { ChargingTransaction chargingTransaction = new ChargingTransaction(); chargingTransaction.MachineId = machine.Id; chargingTransaction.TransactionId = transactionId; chargingTransaction.GunSerNo = (byte)(cmd.connectorId - 1); chargingTransaction.StartChargingDateTime = record.StartChargingDateTime; chargingTransaction.ChargingRecordId = record.Id; chargingTransaction.CreatedOn = DateTime.Now; db.ChargingTransaction.Add(chargingTransaction); db.SaveChanges(); } } } #endregion StartTransactionRequest } //停止充電,接收充电桩上報最新一次充电信息 if (request is StopTransactionRequest) { #region StopTransactionRequest //目前採用DataTransfer的ChargeComplete封包來取代 StopTransactionRequest var cmd = request as StopTransactionRequest; result.Success = true; result.Payload = makeConfirmation.MakeStopTransactionConfirmation(cmd, AuthorizationStatus.Accepted); logger.Trace("reply StopTransactionRequest"); #endregion StopTransactionRequest } //接收電樁DiagnosticsStatus狀態 if (request is DiagnosticsStatusNotificationRequest) { var cmd = request as DiagnosticsStatusNotificationRequest; logger.Trace("Receive 電樁 {0} Staus {1}", client.MachineCustomId, cmd.status.ToString()); result.Success = true; result.Payload = makeConfirmation.MakeDiagnosticsStatusNotificationConfirmation(); } //接收電樁update firmware 進度狀態 if (request is FirmwareStatusNotificationRequest) { var cmd = request as FirmwareStatusNotificationRequest; if ((cmd.status == FirmwareStatus.DownloadFailed) || (cmd.status == FirmwareStatus.InstallationFailed)) { //更新主程序 或 FW 部分 } logger.Trace("Receive 電樁 {0} Staus {1}", client.MachineCustomId, cmd.status.ToString()); result.Success = true; result.Payload = makeConfirmation.MakeFirmwareStatusNotificationConfirmation(); } //處理自訂的訊息 if (request is DataTransferRequest) { var cmd = request as DataTransferRequest; //刷卡認證 if (cmd.messageId == "AuthorizeData") { #region AuthorizeData logger.Trace("Receive Command Status{0}", client.MachineCustomId); var cmd1204 = JsonConvert.DeserializeObject(cmd.data); var c = new HttpClient(); string receivedCard = string.Empty; //判斷此Authorize Reauest是Remote啟動充電 或 刷卡/後台啟動 MemberMapping memberObj = db.MemberCharging.Where(x => x.SelfDefinedId == cmd1204.idTag).AsNoTracking().FirstOrDefault(); if (memberObj != null)//為Remote啟動充電 { receivedCard = memberObj.MemberId; } else //為刷卡或後台啟動 { receivedCard = cmd1204.idTag; } var param = new { CustomerName = client.CustomerName, CardNum = receivedCard, PoleId = client.MachineCustomId, CardPwd = cmd1204.password }; //將卡片資訊送到營運平台驗證 var response = c.PostAsJsonAsync(ApiUrl + @"AuthenticateCard", param).Result; string responseBody = response.Content.ReadAsStringAsync().Result; if (response.IsSuccessStatusCode) { //伺服器回應正常 var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId); var authresult = JsonConvert.DeserializeObject(responseBody); if (authresult.UserMessageCode == 1) //卡片餘額充足 { //卡片認證成功 result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Accepted); #region 下發卡片餘額 IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); #endregion 下發卡片餘額 } else { if (authresult.UserMessageCode == 6) //卡片被凍結 { //卡片認證失敗,檢查卡片是否在充電中 //取得充電樁目前的Status string rqueryCard = string.Empty; if (memberObj != null) //為Remote啟動充電 { rqueryCard = memberObj.MemberId; } else { rqueryCard = cmd1204.idTag; } //若電樁狀態不是"充電中"/"充電結束" ,MachineGun的ReservationCardNum欄位為空白,此時取出的 gunStatusNow 為null var gunStatusNow = db.MachineGun.Where(x => x.ReservationCardNum == rqueryCard && x.MachineId == machine.Id).AsNoTracking().FirstOrDefault(); if (gunStatusNow != null && (gunStatusNow.Status == 2 || gunStatusNow.Status == 3)) { //取得充電樁在充電中或充電結束的狀態,回應有效 result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Accepted); } else { //取得充電樁不在充電中或充電結束的狀態,回應無效 result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Blocked); #region 下發卡片餘額 { IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } #endregion 下發卡片餘額 } } else if (authresult.UserMessageCode == 13) //密碼錯誤 { result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.PasswordError); } else { //卡片為非餘額充足.非凍結的其他狀態 result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Invalid); #region 下發卡片餘額 { IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } #endregion 下發卡片餘額 } } } else { //伺服器異常 result.Payload = makeConfirmation.MakeAuthorizeConfirmation((AuthorizeRequest)request, AuthorizationStatus.Invalid); } result.Success = true; #endregion } if (cmd.messageId == "GunStatus") { #region GunStatus logger.Trace("Receive Command Status{0}", client.MachineCustomId); var cmd104 = JsonConvert.DeserializeObject(cmd.data); WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); //處理104狀態包 //不存在機器,要紀錄起來 var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId); #region mapper ChargingRecordStatus dObj = new ChargingRecordStatus(); dObj.MachineId = machine.Id; dObj.MachineCustomId = machine.CustomId; dObj.Warning = ProcessErrorCode(machine.AC, cmd104.errorCode); dObj.DcV = (decimal)cmd104.dcVoltage; dObj.DcA = (decimal)cmd104.dcCurrent; dObj.BmsV = (decimal)cmd104.bmsVoltage; dObj.BmsA = (decimal)cmd104.bmsCurrent; dObj.AcV_A = (decimal)cmd104.acVoltage_A; dObj.AcV_B = (decimal)cmd104.acVoltage_B; dObj.AcV_C = (decimal)cmd104.acVoltage_C; dObj.AcA_A = (decimal)cmd104.acCurrent_A; dObj.AcA_B = (decimal)cmd104.acCurrent_B; dObj.AcA_C = (decimal)cmd104.acCurrent_C; dObj.BmsWorkingMode = (byte)cmd104.bmsWorkingMode; dObj.CumulativeKwh = (decimal)(cmd104.currentKwh - cmd104.beforeChargingKwh);//(decimal)cmd104.cumulativeKwh; dObj.CumulativeTime = cmd104.cumulativeTime; dObj.CurrentKwh = (decimal)cmd104.currentKwh; dObj.CardBalanceBeforeCharging = (decimal)cmd104.cardBalanceBeforeCharging; dObj.ChargingStrategy = (byte)cmd104.chargingStrategy; dObj.ChargingStrategyParam = (decimal)cmd104.chargingStrategyParameter; dObj.CurrentSoc = (byte)cmd104.currentSOC; dObj.CarConnection = (byte)cmd104.carConnectionStatus; dObj.KwhBeforeCharging = (decimal)cmd104.beforeChargingKwh; dObj.CumulativeChargeFee = (decimal)cmd104.cumulativeChargeFee; dObj.GunAmt = (byte)machine.GunAmt; dObj.GunSerNo = (byte)(cmd104.connectorId - 1); dObj.GunType = (byte)(machine.AC ? 2 : 1); dObj.RemainingTime = cmd104.remainingTime; dObj.Reservation = (byte)(cmd104.isReserved ? 1 : 0); dObj.ReservationOverTime = 0; dObj.StartWith = (byte)cmd104.startWith; dObj.CumulativeOutputKwh = Convert.ToDecimal(cmd104.CumulativeOutputKwh); dObj.GunTemperature = cmd104.GunTemperature; if (cmd104.status == ChargePointStatus.Charging || cmd104.status == ChargePointStatus.Finishing) { dObj.StartChargingDateTime = cmd104.startChargingDateTime; } else { dObj.StartChargingDateTime = null; } dObj.StartChargingType = (byte)cmd104.startChargingType; dObj.Status = (byte)cmd104.status; if (cmd104.startWith == StartWith.Member) { var membertb = db.MemberCharging.Where(c => c.SelfDefinedId == cmd104.idTag).AsNoTracking().FirstOrDefault(); if (membertb != null) { //會員 dObj.MemberId = membertb.MemberId.ToString();//client.ReservationCardNum; dObj.ReservationCardNum = membertb.MemberId.ToString();/*client.ReservationCardNum;*/ } } else if ((cmd104.startWith == StartWith.CardNumber) || (cmd104.startWith == StartWith.Unknown)) { dObj.ReservationCardNum = cmd104.idTag; } MChargingRecordStatus mdObj = new MChargingRecordStatus(); mdObj.MachineId = machine.Id; mdObj.MachineCustomId = machine.CustomId; mdObj.Warning = ProcessErrorCode(machine.AC, cmd104.errorCode); mdObj.DcV = (decimal)cmd104.dcVoltage; mdObj.DcA = (decimal)cmd104.dcCurrent; mdObj.BmsV = (decimal)cmd104.bmsVoltage; mdObj.BmsA = (decimal)cmd104.bmsCurrent; mdObj.AcV_A = (decimal)cmd104.acVoltage_A; mdObj.AcV_B = (decimal)cmd104.acVoltage_B; mdObj.AcV_C = (decimal)cmd104.acVoltage_C; mdObj.AcA_A = (decimal)cmd104.acCurrent_A; mdObj.AcA_B = (decimal)cmd104.acCurrent_B; mdObj.AcA_C = (decimal)cmd104.acCurrent_C; mdObj.BmsWorkingMode = (byte)cmd104.bmsWorkingMode; mdObj.CumulativeKwh = (decimal)(cmd104.currentKwh - cmd104.beforeChargingKwh);//(decimal)cmd104.cumulativeKwh; mdObj.CumulativeTime = cmd104.cumulativeTime; mdObj.CurrentKwh = (decimal)cmd104.currentKwh; mdObj.CardBalanceBeforeCharging = (decimal)cmd104.cardBalanceBeforeCharging; mdObj.ChargingStrategy = (byte)cmd104.chargingStrategy; mdObj.ChargingStrategyParam = (decimal)cmd104.chargingStrategyParameter; mdObj.CurrentSoc = (byte)cmd104.currentSOC; mdObj.CarConnection = (byte)cmd104.carConnectionStatus; mdObj.KwhBeforeCharging = (decimal)cmd104.beforeChargingKwh; mdObj.CumulativeChargeFee = (decimal)cmd104.cumulativeChargeFee; mdObj.GunAmt = (byte)machine.GunAmt; mdObj.GunSerNo = (byte)(cmd104.connectorId - 1); mdObj.GunType = (byte)(machine.AC ? 2 : 1); mdObj.RemainingTime = cmd104.remainingTime; mdObj.Reservation = (byte)(cmd104.isReserved ? 1 : 0); mdObj.ReservationOverTime = 0; mdObj.StartWith = (byte)cmd104.startWith; mdObj.CumulativeOutputKwh = Convert.ToDecimal(cmd104.CumulativeOutputKwh); mdObj.GunTemperature = cmd104.GunTemperature; if (cmd104.status == ChargePointStatus.Charging || cmd104.status == ChargePointStatus.Finishing) { mdObj.StartChargingDateTime = cmd104.startChargingDateTime; } else { mdObj.StartChargingDateTime = null; } mdObj.StartChargingType = (byte)cmd104.startChargingType; mdObj.Status = (byte)cmd104.status; if (cmd104.startWith == StartWith.Member) { var membertb = db.MemberCharging.Where(c => c.SelfDefinedId == cmd104.idTag).AsNoTracking().FirstOrDefault(); if (membertb != null) { //會員 mdObj.MemberId = membertb.MemberId.ToString();//client.ReservationCardNum; mdObj.ReservationCardNum = membertb.MemberId.ToString();/*client.ReservationCardNum;*/ } } else if ((cmd104.startWith == StartWith.CardNumber) || (cmd104.startWith == StartWith.Unknown)) { mdObj.ReservationCardNum = cmd104.idTag; } #endregion mapper GunMessage socketCommand1104 = new GunMessage(); socketCommand1104.chargingRecordStatus = dObj; socketCommand1104.mchargingRecordStatus = mdObj; var process1104Result = wsCommandSrv.Process1104(socketCommand1104); result.Success = true; result.Payload = makeConfirmation.MakeGunStatusConfirmation(); #endregion GunStatus } if (cmd.messageId == "ChargeComplete") { #region ChargeComplete var chargeComplete = JsonConvert.DeserializeObject(cmd.data); WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); //避免同時處理同一個樁號的ChargeComplete lock (_cmd202lst) { if (_cmd202lst.Contains(client.MachineCustomId)) { result.Success = false; return result; } if (!_cmd202lst.Contains(client.MachineCustomId)) { _cmd202lst.Add(client.MachineCustomId); } } logger.Trace("receive ChargeComplete{0}", client.MachineCustomId); var processChargeCompleteResult = wsCommandSrv.ProcessChargeComplete(chargeComplete, client.MachineCustomId); if (processChargeCompleteResult.Success) { result.Success = true; result.Payload = makeConfirmation.MakeChargeCompleteConfirmation(); logger.Trace("reply ChargeComplete"); } else { result.Success = false; result.Exception = processChargeCompleteResult.Exception; result.Message = processChargeCompleteResult.Message; logger.Error(processChargeCompleteResult.Exception, "ChargeComplete error"); } lock (_cmd202lst) { _cmd202lst.Remove(client.MachineCustomId); } #endregion ChargeComplete } if (cmd.messageId == "Location") { #region Location var data = JsonConvert.DeserializeObject(cmd.data); WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); logger.Trace("receive 1120 {0}", client.MachineCustomId); logger.Trace("receive 1120 {0} {1} {2}", client.MachineCustomId, data.longitude, data.latitude); var processLocationResult = wsCommandSrv.ProcessLocation(client.MachineCustomId, data); if (processLocationResult.Success) { result.Success = true; result.Payload = makeConfirmation.MakeLocationConfirmation(); logger.Trace("reply Location"); } else { result.Success = false; result.Exception = processLocationResult.Exception; logger.Error(processLocationResult.Exception, "Location.conf error"); } #endregion Location } if (cmd.messageId == "ChargingGunBMS") { #region ChargingGunBMS var data = JsonConvert.DeserializeObject(cmd.data); WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); var processChargingGunBMSResult = wsCommandSrv.ProcessChargingGunBMS(client.MachineId, data); if (processChargingGunBMSResult.Success) { result.Success = true; result.Payload = makeConfirmation.MakeChargingGunBMSConfirmation(); logger.Trace("reply ChargingGunBMS"); } #endregion ChargingGunBMS } //電樁上報升級文件的版 if (cmd.messageId == "UpgradeVersion") { #region UpgradeVersion UploadFile uploadFile = null; WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp); var data = JsonConvert.DeserializeObject(cmd.data); var processUpgradeVersionResult = wsCommandSrv.CheckCmd2104(client.MachineId, data, out uploadFile); result.Success = true; result.Payload = makeConfirmation.MakeUpgradeVersionConfConfirmation(processUpgradeVersionResult, data.param); logger.Trace("reply DataTransfer.conf"); //DC 桩 if (uploadFile != null) { //製作 UpdateFirmware Request if (((int)data.param == 1 || CmdHelper.GetCmd2101Type((byte)data.param) == 2) && processUpgradeVersionResult == false) { int type = CmdHelper.GetCmd2101Type((byte)data.param); IRequest req = makeRequest.MakeUpdateFirmware(type, data.param, uploadFile); string uuid = String.Empty; lock (_clientQueue) { uuid = client.queue.store(req); } result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } } #endregion UpgradeVersion } } } catch (Exception ex) { logger.Error(ex); result.Exception = ex; result.Success = false; } return result; } /// /// 判斷是否由會員帳號充電 /// /// OCPP Request /// Data Base Entity /// public bool IsMemberCharging(IRequest request, PhihongDbContext db) { bool result = new Boolean(); IUnitOfWork uowtemp = new UnitOfWork(db); IMachineService machineSrvTemp = new MachineService(uowtemp); ISocketCommandService socketCommandSrvTemp = new SocketCommandService(uowtemp); ICustomerService _customerServiceTemp = new CustomerService(); try { //刷卡認證 if (request is AuthorizeRequest) { var cmd = request as AuthorizeRequest; //判斷此Authorize Reauest是Remote啟動充電 或 刷卡/後台啟動 MemberMapping memberObj = db.MemberCharging.Where(x => x.SelfDefinedId == cmd.idTag).AsNoTracking().FirstOrDefault(); if (memberObj != null)//為Remote啟動充電 { result = true; } else //為刷卡或後台啟動 { result = false; } } } catch (Exception ex) { logger.Error(ex); result = false; } return result; } /// /// 處理電樁所送的comfirm message /// /// OCPP Message uniqueId /// OCPP Confirm Message /// Data Base Entity /// Connected Client Data /// public OCPPResult HandleConfirmation(string uniqueId, IConfirmation confirmation, string callResultAction, PhihongDbContext db, ref ClientData client) { OCPPResult result = new OCPPResult(); IUnitOfWork uowconf = new UnitOfWork(db); IRepository _mvfRepo = uowconf.Repository(); try { #region Core //接收電樁對Remote啟動充電回應 if (confirmation is RemoteStartTransactionConfirmation) { IConfirmation cmd = confirmation as RemoteStartTransactionConfirmation; } //接收電樁對Remote停止充電回應 if (confirmation is RemoteStopTransactionConfirmation) { IConfirmation cmd = confirmation as RemoteStopTransactionConfirmation; } //接收電樁對Unlock Connector回應 if (confirmation is UnlockConnectorConfirmation) { IConfirmation cmd = confirmation as UnlockConnectorConfirmation; } //接收電樁對configuration 回應 if (confirmation is GetConfigurationConfirmation) { GetConfigurationConfirmation cmd = confirmation as GetConfigurationConfirmation; foreach (var config in cmd.configurationKey) { var item = client.configurationKey.Where(c => c.key == config.key).FirstOrDefault(); item.value = config.value; } } //接收電樁對電樁槍號 availability 改變 回應 if (confirmation is ChangeAvailabilityConfirmation) { ChangeAvailabilityConfirmation cmd = confirmation as ChangeAvailabilityConfirmation; } #endregion Core #region FirmwareManagement //接收電樁對取得 GetDiagnostics資訊回應 if (confirmation is GetDiagnosticsConfirmation) { GetDiagnosticsConfirmation cmd = confirmation as GetDiagnosticsConfirmation; logger.Trace("Receive 電樁 {0} 上傳檔案 {1}", client.MachineCustomId, cmd.fileName); } //接收電樁對UpdateFirmware回應 if (confirmation is UpdateFirmwareConfirmation) { UpdateFirmwareConfirmation cmd = confirmation as UpdateFirmwareConfirmation; } #endregion FirmwareManagement #region LocalAuthListManagement //接收電樁對 Get Local List Version 回應 if (confirmation is GetLocalListVersionConfirmation) { GetLocalListVersionConfirmation cmd = confirmation as GetLocalListVersionConfirmation; string customId = client.MachineCustomId; var localauthTable = db.LocalAuthorization.Where(c => c.CustomId == customId).FirstOrDefault(); if (localauthTable != null) { localauthTable.CurrentVersion = cmd.listVersion; localauthTable.UpdatedOn = DateTime.Now; db.SaveChanges(); } } //接收電樁對 Send Local List 回應 if (confirmation is SendLocalListConfirmation) { SendLocalListConfirmation cmd = confirmation as SendLocalListConfirmation; if (cmd.updateStatus == UpdateStatus.Accepted) { //下發GetLocalListVersion.req Cmd1011 cmd1011 = client.CmdHelper.Create(1011) as Cmd1011; cmd1011.CmdSerNum = client.GetCmdSerNum(); cmd1011.SerNum = client.GetSerNum(); cmd1011.Pack(); ServerCommand sc = new ServerCommand(); sc.OutCmdNum = cmd1011.Cmd; sc.CreatedOn = DateTime.Now; sc.MachineId = client.MachineId; sc.MachineCustomId = client.MachineCustomId; sc.OutTransData = cmd1011.byteList.ToArray(); db.ServerCommand.Add(sc); db.SaveChanges(); } } #endregion LocalAuthListManagement #region Reservation //接收電樁對 預約 回應 if (confirmation is ReserveNowConfirmation) { ReserveNowConfirmation cmd = confirmation as ReserveNowConfirmation; } //接收電樁對 取消預約 回應 if (confirmation is CancelReservationConfirmation) { CancelReservationConfirmation cmd = confirmation as CancelReservationConfirmation; } #endregion Reservation #region SmartCharging //接收電樁對 Charging Profile回應 if (confirmation is SetChargingProfileConfirmation) { SetChargingProfileConfirmation cmd = confirmation as SetChargingProfileConfirmation; var uuid = Guid.Parse(uniqueId); var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault(); if (chargingProfile != null) { chargingProfile.UpdatedOn = DateTime.Now; chargingProfile.ResponseStatus = (int)cmd.status; db.SaveChanges(); } logger.Trace("Receive 電樁 {0} Charging Profile回應 Status:{1} \n", client.MachineCustomId, cmd.status); } //接收電樁對清除Charging Profile回應 if (confirmation is ClearChargingProfileConfirmation) { ClearChargingProfileConfirmation cmd = confirmation as ClearChargingProfileConfirmation; var uuid = Guid.Parse(uniqueId); var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault(); if (chargingProfile != null) { chargingProfile.UpdatedOn = DateTime.Now; chargingProfile.ResponseStatus = (int)cmd.status; db.SaveChanges(); } logger.Trace("Receive 電樁 {0} 清除Charging Profile Status:{1} \n", client.MachineCustomId, cmd.status); } //接收電樁對Composite Charging Schedule 回應 if (confirmation is GetCompositeScheduleConfirmation) { GetCompositeScheduleConfirmation cmd = confirmation as GetCompositeScheduleConfirmation; if (cmd.chargingSchedule != null) { logger.Trace("Receive 電樁 {0} Composite Charging Schedule connectorId:{1} Status:{2} ScheduleStart:{3} duration:{4} chargingRateUnit:{5} minChargingRate:{6}\n", client.MachineCustomId, cmd.connectorId, cmd.status, cmd.scheduleStart.ToLocalTime(), cmd.chargingSchedule.duration, cmd.chargingSchedule.chargingRateUnit, cmd.chargingSchedule.minChargingRate); foreach (var period in cmd.chargingSchedule.chargingSchedulePeriod) { logger.Trace("startPeriod:{0} limit:{1} numberPhases:{2}\n", period.startPeriod, period.limit, period.numberPhases); } } else logger.Trace("Receive 電樁 {0} Composite Charging Schedule connectorId:{1} Status:{2} ScheduleStart:{3} \n", client.MachineCustomId, cmd.connectorId, cmd.status, cmd.scheduleStart.ToLocalTime()); } #endregion SmartCharging #region DataTransfer //處理自訂的訊息 if (confirmation is DataTransferConfirmation) { DataTransferConfirmation cmd = confirmation as DataTransferConfirmation; if (cmd.data != null) { if (callResultAction.Contains("SetChargingConfig")) { var uuid = Guid.Parse(uniqueId); var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault(); if (chargingProfile != null) { chargingProfile.UpdatedOn = DateTime.Now; chargingProfile.ResponseStatus = (int)cmd.status; db.SaveChanges(); } } else if (callResultAction.Contains("GetChargingConfig")) { var uuid = Guid.Parse(uniqueId); var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault(); if (chargingProfile != null) { var charingRate = JsonConvert.DeserializeObject(cmd.data); chargingProfile.UpdatedOn = DateTime.Now; chargingProfile.ResponseStatus = (int)cmd.status; chargingProfile.EasyConfig = String.Format("{0}:{1}", charingRate.chargingRateUnit, charingRate.limit); db.SaveChanges(); } } else if (cmd.data[0] == '{') { JObject restoredObject = JObject.Parse(cmd.data); FirmwareMD5 fwmd5 = new FirmwareMD5(); if (cmd.data.Contains("md5") == true) { // Copy to a static fwmd5 instance fwmd5 = restoredObject.ToObject(); //充電樁對服務器下發升級指令的應答 //處理 UpdateFirmware conf fwmd5.md5 = fwmd5.md5.ToUpper(); string machineId = client.MachineId; var mvFile = _mvfRepo.Query(c => c.MachineVersion.MachineId.Equals(machineId) && c.UploadFile.FileMD5.ToUpper().Equals(fwmd5.md5)).Select(c => c).OrderByDescending(c => c.CreatedOn).FirstOrDefault(); if (mvFile != null) { mvFile.DownloadedOn = DateTime.Now; uowconf.SaveChanges(); result.Success = true; } else { throw new ApplicationException("找不到相關檔案"); } } } else { if (cmd.data == "true") //成功 { logger.Trace("Receive 電樁 {0} 處理電費/服務費 結果 {1}", client.MachineCustomId, cmd.data); } else if (cmd.data == "false") //失敗 { logger.Trace("Receive 電樁 {0} 處理電費/服務費 結果 {1}", client.MachineCustomId, cmd.data); } } } } #endregion DataTransfer result.Success = true; } catch (Exception ex) { logger.Error(ex); result.Exception = ex; result.Success = false; } return result; } //處理電樁所送的Error Message public OCPPResult HandleError(string uniqueId, string errorCode, string errorDescription, object payload) { OCPPResult result = new OCPPResult(); return result; } /// /// 處理WSServer 產生DataTransfer的ServerDomainName命令 /// /// Message Queue /// ServerDomainName /// public string HandleServerDomainName(ref Queue queue, string ServerDomainName) { IRequest req = makeRequest.MakeServerDomainNameRequest(ServerDomainName); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } return GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } /// /// 處理WSServer 產生DataTransfer的UpdateFirmware命令 /// /// Message Queue /// Messgae Type /// ProgramParam Type /// UploadFile /// public string HandleUpdateFirmware(ref Queue queue, int type, ProgramParam param, UploadFile uploadFile) { IRequest req = makeRequest.MakeUpdateFirmware(type, param, uploadFile); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } return GenerateCall(uuid, Actions.DataTransfer.ToString(), req); } /// /// 處理WSServer 產生OCPP協議的UpdateFirmware命令 /// /// Message Queue /// FileURL /// public string HandleOCPPUpdateFirmware(ref Queue queue, string fileurl) { IRequest req = MakeRequestHandler.MakeOCPPUpdateFirmwareRequest(fileurl); string uuid = String.Empty; lock (_clientQueue) { uuid = queue.store(req); } return GenerateCall(uuid, Actions.UpdateFirmware.ToString(), req); } /// /// 將後台要傳給電樁的request轉換成Call Message /// /// OCPP Call Message uniqueId /// OCPP Call Message action /// Request Message /// public string GenerateCall(string uniqueId, string action, object request) { try { return MakeCall(uniqueId, action, PackPayload(request)); } catch (Exception ex) { logger.Error(ex); throw new ApplicationException(ex.ToString()); } } /// /// 將後台要傳給電樁的comfirm轉換成CallResult Message /// /// OCPP Call Message uniqueId /// Confirmation Message /// public string GenerateCallResult(string uniqueId, object confirmation) { try { return MakeCallResult(uniqueId, PackPayload(confirmation)); } catch (Exception ex) { logger.Error(ex); throw new ApplicationException(ex.ToString()); } } /// /// 將後台要傳給電樁的錯誤訊息轉換成CallError Message /// /// OCPP CallError Message uniqueId /// OCPP CallError Message errorCode /// OCPP CallError Message errorDescription /// public string GenerateCallError(string uniqueId, string errorCode, string errorDescription) { try { return MakeCallError(uniqueId, errorCode, errorDescription); } catch (Exception ex) { logger.Error(ex); throw new ApplicationException(ex.ToString()); } } /// /// 產生CallResult Message /// /// OCPP CallResult Message uniqueId /// OCPP CallResult Message payload /// private string MakeCallResult(string uniqueId, object payload) { const string CALLRESULT_FORMAT = "[3,\"{0}\",{1}]"; return string.Format(CALLRESULT_FORMAT, uniqueId, payload.ToString()); } /// /// 產生Call Message /// /// OCPP CallResult Message uniqueId /// OCPP CallResult Message action /// OCPP CallResult Message payload /// private string MakeCall(string uniqueId, string action, object payload) { const string CALL_FORMAT = "[2,\"{0}\",\"{1}\",{2}]"; return string.Format(CALL_FORMAT, uniqueId, action, payload.ToString()); } /// /// 產生CallError Message /// /// OCPP CallError Message uniqueId /// OCPP CallError Message errorCode /// OCPP CallError Message errorDescription /// private string MakeCallError(string uniqueId, string errorCode, string errorDescription) { const string CALLERROR_FORMAT = "[4,\"{0}\",\"{1}\",\"{2}\",{3}]"; return string.Format(CALLERROR_FORMAT, uniqueId, errorCode, errorDescription, "{}"); } /// /// 處理OCPP CallResult Message /// /// OCPP CallResult Message uniqueId /// OCPP CallResult Message payload /// OCPP CallResult Message queue /// OCPP CallResult Message messageType /// public OCPPResult OnCallResult(string id, object payload, ref Queue queue, out string messageType) { OCPPResult result = new OCPPResult(); try { IRequest request; Type actionType = GetConfirmationType(ref queue, id, out request); if (request == null) { messageType = "There are two conditions. Maybe it was sent repeatedly or there is no related Request. "; result.Message = "none"; result.Success = false; } else { IConfirmation confirmation = (IConfirmation)UnpackPayload(payload, actionType); if (actionType.Name.Contains("DataTransferConfirmation")) { messageType = "DataTransfer Confirmation(" + ((DataTransferRequest)request).messageId + ")"; } else if (actionType.Name.Contains("TriggerMessageConfirmation")) { messageType = "TriggerMessage Confirmation(" + ((TriggerMessageRequest)request).requestedMessage.ToString() + ")"; } else { messageType = actionType.Name.Remove(actionType.Name.IndexOf("Confirmation")) + " Confirmation"; } if (confirmation.Validate()) { result.Payload = confirmation; result.Success = true; } else { result.Message = GenerateCallError(id, "OccurenceConstraintViolation", "Payload for Action is syntactically correct but at least one of the fields violates occurence constraints"); result.Success = false; } } } catch (Exception ex) { logger.Error(ex); result.Exception = ex; messageType = string.Empty; result.Success = false; } return result; } /// /// 處理OCPP CALL Message /// /// OCPP CALL Message uniqueId /// OCPP CALL Message action /// OCPP CALL Message payload /// public OCPPResult OnCall(string id, string action, object payload) { OCPPResult result = new OCPPResult(); Feature feature = featureHandler.FindFeatureByAction(action); if (feature == null) { result.Message = GenerateCallError(id, "NotImplemented", "Requested Action is not known by receiver"); result.Success = false; } else { try { IRequest request = (IRequest)UnpackPayload(payload, feature.GetRequestType()); if (request.Validate()) { result.Payload = request; result.Success = true; } else { result.Message = GenerateCallError(id, "OccurenceConstraintViolation", "Payload for Action is syntactically correct but at least one of the fields violates occurence constraints"); result.Success = false; } } catch (Exception ex) { logger.Error(ex); result.Exception = ex; result.Success = false; } } return result; } /// /// 處理OCPP Error Message /// /// OCPP Error Message uniqueId /// OCPP Error Message errorCode /// OCPP Error Message errorDescription /// OCPP Error Message payload /// public OCPPResult OnError(string id, string errorCode, string errorDescription, object payload) { OCPPResult result = new OCPPResult(); result.Success = true; return result; } } }