using EVCB_OCPP.Packet.Features; using EVCB_OCPP.Packet.Messages; using EVCB_OCPP.Packet.Messages.Basic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using OCPPServer.Protocol; using System; using System.Collections.Generic; namespace EVCB_OCPP.WSServer.Message { /// /// 實現 OCPP16 基本傳送規範, /// 1.訊息 基本格式,將訊息包裝成 Call 、CallResult、CallError 三種格式 /// 2.OCPP 定義的傳送規則:交易相關的訊息必須依照時序性傳送,一個傳完才能接著送下一個(忽略規則 由Center System定義) /// internal class OCPP16MessageHandler { static protected ILogger logger = NLog.LogManager.GetCurrentClassLogger(); #region 傳送 or 解析訊息需要欄位 private const int INDEX_MESSAGEID = 0; private const int INDEX_UNIQUEID = 1; internal const int TYPENUMBER_CALL = 2; private const int INDEX_CALL_ACTION = 2; private const int INDEX_CALL_PAYLOAD = 3; internal const int TYPENUMBER_CALLRESULT = 3; private const int INDEX_CALLRESULT_PAYLOAD = 2; internal const int TYPENUMBER_CALLERROR = 4; private const int INDEX_CALLERROR_ERRORCODE = 2; private const int INDEX_CALLERROR_DESCRIPTION = 3; private const int INDEX_CALLERROR_PAYLOAD = 4; private const string CALL_FORMAT = "[2,\"{0}\",\"{1}\",{2}]"; private const string CALLRESULT_FORMAT = "[3,\"{0}\",{1}]"; private const string CALLERROR_FORMAT = "[4,\"{0}\",\"{1}\",\"{2}\",{3}]"; private const string DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; private const string DATE_FORMAT_WITH_MS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; #endregion private List profiles = new List() { new CoreProfile(), new FirmwareManagementProfile(), new ReservationProfile(), new RemoteTriggerProfile(), new SmartChargingProfile(), new LocalAuthListManagementProfile(), new SecurityProfile() }; /// /// 將收到的封包做基本的拆解分成 Call 、CallResult、CallError /// /// /// /// internal MessageResult AnalysisReceiveData(ClientData client, string data) { MessageResult result = new MessageResult(); try { var msg = Parse(data); if (msg != null) { result.UUID = msg.Id; switch (msg.TypeId) { case TYPENUMBER_CALL: { //只有CallMessage 才有在RawData有Action BasicMessageResult baseResult = UnPackPayloadbyCall(msg.Action, msg.Payload.ToString()); Actions action = Actions.None; Enum.TryParse(msg.Action, out action); result.Action = msg.Action; if (baseResult.Request != null) { if (baseResult.Request.Validate()) { result.Id = TYPENUMBER_CALL; result.Message = baseResult.Request; } else { string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(), OCPPErrorDescription.OccurenceConstraintViolation); result.Id = TYPENUMBER_CALL; result.Message = baseResult.Request; result.Success = false; result.CallErrorMsg = replyMsg; result.Exception = new Exception("Validation Failed"); } } else { string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation, OCPPErrorDescription.OccurenceConstraintViolation); result.Id = TYPENUMBER_CALL; result.Message = baseResult.Request; result.Success = false; result.CallErrorMsg = replyMsg; result.Exception = baseResult.Exception; } } break; case TYPENUMBER_CALLRESULT: { BasicMessageResult baseResult = UnPackPayloadbyCallResult(client.queue, msg.Id, msg.Payload.ToString()); if (baseResult.Confirmation != null) { if (baseResult.Confirmation.Validate()) { result.Id = TYPENUMBER_CALLRESULT; result.Message = baseResult.Confirmation; result.Action = baseResult.Confirmation.GetRequest().Action; //return data } else { string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(), OCPPErrorDescription.OccurenceConstraintViolation); result.Id = TYPENUMBER_CALLRESULT; result.Message = baseResult.Confirmation; result.Success = false; result.CallErrorMsg = replyMsg; result.Exception = new Exception("Validate Failed"); } } else { string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(), OCPPErrorDescription.OccurenceConstraintViolation); result.Id = TYPENUMBER_CALLRESULT; result.Message = baseResult.Confirmation; result.Success = false; result.CallErrorMsg = replyMsg; result.Exception = baseResult.Exception; } } break; case TYPENUMBER_CALLERROR: { result.Id = TYPENUMBER_CALLERROR; var sentRequest = UnPackPayloadbyCallError(client.queue, msg.Id); if (sentRequest != null) { IRequest request = sentRequest as IRequest; result.Action = request.Action; result.Message = sentRequest; result.ReceivedErrorCode = string.Format("ErrorMsg {0}:{1}", ((CallErrorMessage)msg).ErrorCode, ((CallErrorMessage)msg).ErrorDescription); } } break; default: break; } // if (msg != null) Console.WriteLine(string.Format("Receieved Message : {0}", msg.ToString())); } } catch (Exception ex) { if (string.IsNullOrEmpty(result.UUID)) { result.UUID = data.Substring(4, 39); result.UUID = result.UUID.Split(new string[] { "\"," }, StringSplitOptions.None)[0]; } result.Success = false; result.Exception = ex; } return result; } #region 解析收到的訊息 /// /// Parse data to OCPP Basic Message /// /// /// private BaseMessage Parse(string message) { try { if (message.StartsWith("[4,\"")) { message = message.Replace('{', '"'); message = message.Replace('}', '"'); } 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(); callError.ErrorDetails = 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("Parse Error=> Problem: {0}", ex.Message)); } } private BasicMessageResult UnPackPayloadbyCall(string action, string payload) { BasicMessageResult result = new BasicMessageResult(); try { Feature feature = null; foreach (var profile in profiles) { feature = profile.GetFeaturebyAction(action); if (feature == null) { continue; } else { break; } } result.Request = JsonConvert.DeserializeObject(payload, feature.GetRequestType()) as IRequest; } catch (Exception ex) { result.Exception = ex; logger.Error(string.Format("[{0}]UnPackPayloadbyCall Ex: {1}", action, ex.Message), "UnPack"); } return result; } private BasicMessageResult UnPackPayloadbyCallResult(Queue requestQueue, string uniqueId, string payload) { int i = 1; BasicMessageResult result = new BasicMessageResult(); try { i = -1 ; IRequest request = requestQueue.RestoreRequest(uniqueId); if (request == null) { result.Exception = new Exception("Can't find request"); return result; } i = 2; Feature feature = null; foreach (var profile in profiles) { i = 3; feature = profile.GetFeaturebyType(request.GetType()); i = 4; if (feature == null) { continue; } else { break; } } i = 5; IConfirmation confrim = JsonConvert.DeserializeObject(payload, feature.GetConfirmationType()) as IConfirmation; i = 6; confrim.SetRequest(request); i = 7; result.Confirmation = confrim; } catch (Exception ex) { result.Exception = ex; logger.Error(string.Format(i+"UnPackPayloadbyCallResult Data:[{0},{1}] Ex: {2}", uniqueId, payload, ex.ToString()), "UnPack"); } return result; } private IRequest UnPackPayloadbyCallError(Queue requestQueue, string uniqueId) { IRequest sentMsg = requestQueue.RestoreRequest(uniqueId); return sentMsg; } #endregion } }