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
}
}