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; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EVCB_OCPP.WSServer.Message { /// /// 實現 OCPP 基本傳送規範, /// 1.訊息 基本格式,將訊息包裝成 Call 、CallResult、CallError 三種格式 /// 2.OCPP 定義的傳送規則:交易相關的訊息必須依照時序性傳送,一個傳完才能接著送下一個(忽略規則 由Center System定義) /// internal class BasicMessageHandler { 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() }; /// /// 將收到的封包做基本的拆解分成 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 = 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("Validate Failed"); } } else { string replyMsg = 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 = 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 = 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: { var sentRequest = UnPackPayloadbyCallError(client.queue, msg.Id); if (sentRequest != null) { IRequest request = sentRequest as IRequest; result.Action = request.Action; result.Id = TYPENUMBER_CALLERROR; 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) { result.Success = false; result.Exception = ex; } return result; } #region 解析收到的訊息 /// /// Parse data to OCPP Basic Message /// /// /// private BaseMessage Parse(string message) { 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(); 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("Error=>Communicator.Parse: {0}", ex.ToString())); } } 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("UnPackPayloadbyCall Ex: {0}", ex.ToString()), "UnPack"); } return result; } private BasicMessageResult UnPackPayloadbyCallResult(Queue requestQueue, string uniqueId, string payload) { BasicMessageResult result = new BasicMessageResult(); try { IRequest request = requestQueue.RestoreRequest(uniqueId); Feature feature = null; foreach (var profile in profiles) { feature = profile.GetFeaturebyType(request.GetType()); if (feature == null) { continue; } else { break; } } IConfirmation confrim = JsonConvert.DeserializeObject(payload, feature.GetConfirmationType()) as IConfirmation; confrim.SetRequest(request); result.Confirmation = confrim; } catch (Exception ex) { result.Exception = ex; logger.Error(string.Format("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 internal string GenerateCallError(string uniqueId, string errorCode, string errorDescription) { string msg = string.Format(CALLERROR_FORMAT, uniqueId, errorCode, errorDescription, "{}"); return msg; } internal string GenerateConfirmation(string uniqueId, IConfirmation confirmation) { string msg = string.Empty; if (confirmation != null && confirmation.Validate()) { msg = string.Format(CALLRESULT_FORMAT, uniqueId, JsonConvert.SerializeObject(confirmation, new JsonSerializerSettings() { NullValueHandling= NullValueHandling.Ignore, Formatting = Formatting.None })); } else { logger.Error(string.Format("confirmation is null or InVaild in GenerateConfirmation Method"), "Warning"); } return msg; } internal string GenerateRequest(string uniqueId, string action, IRequest request) { string msg = string.Empty; if (request != null && request.Validate()) { msg = string.Format(CALL_FORMAT, uniqueId, action, JsonConvert.SerializeObject(request, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None })); } else { logger.Error(string.Format("confirmation is null or InVaild in GenerateRequest Method"), "Warning"); } return msg; } } }