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