using EVCB_OCPP.Packet.Messages; using EVCB_OCPP.Packet.Messages.Basic; using EVCB_OCPP20.Packet.Features; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using OCPPServer.Protocol; using System; using I20Confirmation = EVCB_OCPP20.Packet.Messages.IConfirmation; using I20Request = EVCB_OCPP20.Packet.Messages.IRequest; namespace EVCB_OCPP.WSServer.Message { /// /// 實現 OCPP20 基本傳送規範, /// 1.訊息 基本格式,將訊息包裝成 Call 、CallResult、CallError 三種格式 /// 2.OCPP 定義的傳送規則:交易相關的訊息必須依照時序性傳送,一個傳完才能接著送下一個(忽略規則 由Center System定義) /// internal class OCPP20MessageHandler { 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 /// /// 將收到的封包做基本的拆解分成 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.Request20 != null) { if (baseResult.Request20.Validate()) { result.Id = TYPENUMBER_CALL; result.Message = baseResult.Request20; } else { string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(), OCPPErrorDescription.OccurenceConstraintViolation); result.Id = TYPENUMBER_CALL; result.Message = baseResult.Request20; 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.Request20; result.Success = false; result.CallErrorMsg = replyMsg; result.Exception = baseResult.Exception; } } break; case TYPENUMBER_CALLRESULT: { BasicMessageResult baseResult = UnPackPayloadbyCallResult(client.queue20, msg.Id, msg.Payload.ToString()); if (baseResult.Confirmation20 != null) { if (baseResult.Confirmation20.Validate()) { result.Id = TYPENUMBER_CALLRESULT; result.Message = baseResult.Confirmation20; result.Action = baseResult.Confirmation20.GetRequest().Action; //return data } else { string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(), OCPPErrorDescription.OccurenceConstraintViolation); result.Id = TYPENUMBER_CALLRESULT; result.Message = baseResult.Confirmation20; 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.Confirmation20; 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) //{ // I20Request request = sentRequest as I20Request; // 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=> {0} Problem: {0}", message, ex.Message)); } } private BasicMessageResult UnPackPayloadbyCall(string action, string payload) { BasicMessageResult result = new BasicMessageResult(); try { result.Request20 = (I20Request)(JsonConvert.DeserializeObject(payload, Type.GetType("EVCB_OCPP20.Packet.Messages." + action + "Request,EVCB_OCPP20.Packet"))); } catch (Exception ex) { result.Exception = ex; logger.Error(string.Format("[{0}]UnPackPayloadbyCall Ex: {1}", action, ex.Message), "UnPack"); } return result; } private BasicMessageResult UnPackPayloadbyCallResult(EVCB_OCPP20.Packet.Messages.Basic.Queue requestQueue, string uniqueId, string payload) { BasicMessageResult result = new BasicMessageResult(); try { I20Request request = requestQueue.RestoreRequest(uniqueId); I20Confirmation confrim = JsonConvert.DeserializeObject(payload, Type.GetType("EVCB_OCPP20.Packet.Messages." + request.Action + "Response,EVCB_OCPP20.Packet")) as I20Confirmation; confrim.SetRequest(request); result.Confirmation20 = confrim; } catch (Exception ex) { result.Exception = ex; logger.Error(string.Format("UnPackPayloadbyCallResult Data:[{0},{1}] Ex: {2}", uniqueId, payload, ex.ToString()), "UnPack"); } return result; } #endregion } }