You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

703 lines
23 KiB

6 months ago
using System.Text;
using Autofac;
using Autofac.Core;
using Common.Util;
6 months ago
using Entity.DbModel.Station;
using HybirdFrameworkCore.Autofac;
using HybirdFrameworkCore.Autofac.Attribute;
using HybirdFrameworkDriver.Common;
using log4net;
using MQTTnet;
6 months ago
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving;
6 months ago
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Utilities;
6 months ago
using Repository.Station;
using Service.Cloud.Handler;
using Service.Cloud.Msg;
6 months ago
using Service.Cloud.Msg.Cloud.Req;
using Service.Cloud.Msg.Cloud.Resp;
5 months ago
using Service.Cloud.Msg.Cloud.Resp.OutCharger;
using Service.Cloud.Msg.Host.Req;
5 months ago
using Service.Cloud.Msg.Host.Req.OutCharger;
using Service.Cloud.Msg.Host.Resp;
6 months ago
namespace Service.Cloud.Client;
[Scope("SingleInstance")]
public class CloudClient : IMqttClientConnectedHandler, IMqttApplicationMessageReceivedHandler,
IMqttClientDisconnectedHandler
6 months ago
{
private static readonly ILog Log = LogManager.GetLogger(typeof(CloudClient));
6 months ago
public SwapOrderRepository SwapOrderRepository { get; set;}
#region tcp param
6 months ago
public string ServerIp { get; set; }
public int ServerPort { get; set; }
public string ClientId { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
public int KeepalivePeriod { get; set; } = 3000;
public int Timeout { get; set; } = 60;
public string MqttVersion { get; set; } = "4.0.0";
public bool IsCleanSession { get; set; } = false;
6 months ago
#endregion
#region property
public bool Connected { get; set; }
public bool AutoReConnect { get; set; }
public string StationNo { get; set; }
public string SubTopic { get; set; }
public string PubTopic { get; set; }
public int Encrypt { get; set; }
public string? AesKey { get; set; }
6 months ago
public bool Authed { get; set; }
#endregion
#region Cmd msg cache
6 months ago
public CarCanStart? CarCanStart { get; set; }
/// <summary>
/// 站端发-云端收
/// </summary>
public MsgPair<CarAuth, CarAuthRes> CarAuth { get; set; } = new();
public MsgPair<CardataReport, CardataReportRes> VehicleData { get; set; } = new();
public MsgPair<SignIn, SignInRes> Sign { get; set; } = new();
public MsgPair<DevList, DevListRes> ReportingDevice { get; set; } = new();
public MsgPair<StaSwapRecord, StaSwapRecordRes> UploadPowerChange { get; set; } = new();
public MsgPair<ChargeRecordReport, ChargeRecordReportRes> ChargeRecord { get; set; } = new();
public MsgPair<StationRunStatus, StationRunStatusRes> HostStatus { get; set; } = new();
public MsgPair<StationChnRunStatus, StationChnRunStatusRes> ChannelStatus { get; set; } = new();
public MsgPair<FaultReport, FaultReportRes> RealTimeFault { get; set; } = new();
public MsgPair<EvmDataInfo, EvmDataInfoRes> TemperatureHumidity { get; set; } = new();
public MsgPair<AcDataInfo, AcDataInfoRes> AirConditioning { get; set; } = new();
public MsgPair<EqmStateStartLogInfo, EqmStateStartLogInfoRes> StartLog { get; set; } = new();
public MsgPair<EqmStateEndLogInfo, EqmStateEndLogInfoRes> EndLog { get; set; } = new();
public MsgPair<ChargeDevDataInfo, ChargeDevDataInfoRes> ChargeDevDataInfo { get; set; } = new();
public MsgPair<BatDataInfo, BatDataInfoRes> BatData { get; set; } = new();
6 months ago
public MsgPair<ChargeRecordUpLoad, ChargeRecordUploadRes> ChargeRecordUpLoad { get; set; } = new();
public MsgPair<BatteryTotal, BatteryTotalRes> BatteryTotal { get; set; } = new();
public MsgPair<ChargingTotalDis, ChargingTotalDisRes> ChargingTotalDis { get; set; } = new();
public MsgPair<PowerTotal, PowerTotalRes> PowerTotal { get; set; } = new();
public MsgPair<StaChargingTotal, StaChargingTotalRes> StaChargingTotal { get; set; } = new();
public MsgPair<MeterEnergyKwh, MeterEnergyKwhRes> MeterEnergyKwh { get; set; } = new();
public MsgPair<MeterDayEnergyVal, MeterDayEnergyValRes> MeterDayEnergyVal { get; set; } = new();
public MsgPair<StaHourEnergyVal, StaHourEnergyValRes> StaHourEnergyVal { get; set; } = new();
public MsgPair<StaDayEnergyVal, StaDayEnergyValRes> StaDayEnergyVal { get; set; } = new();
public MsgPair<StaDayOpeEnergyVal, StaDayOpeEnergyValRes> StaDayOpeEnergyVal { get; set; } = new();
public MsgPair<StaHourAmountVal, StaHourAmountValRes> StaHourAmountVal { get; set; } = new();
/// <summary>
/// 云端发-站端收
/// </summary>
public MsgPair<CarCanStart, CarCanStartRes> CarCanStartInfo { get; set; } = new();
public MsgPair<SetStBaseInfo, SetStBaseInfoRes> SetStBaseInfo { get; set; } = new();
public MsgPair<SetService, SetServiceRes> SetService { get; set; } = new();
public MsgPair<SetOpTime, SetOpTimeRes> SetOpTime { get; set; } = new();
public MsgPair<SetOpModel, SetOpModelRes> SetOpModel { get; set; } = new();
public MsgPair<SetStaPrice, SetStaPriceRes> SetStaPrice { get; set; } = new();
public MsgPair<SetChargePrice, SetChargePriceRes> SetChargePrice { get; set; } = new();
public MsgPair<SetChangeCarData, SetChangeCarDataRes> SetChangeCarData { get; set; } = new();
public MsgPair<SetConfig, SetConfigRes> SetConfig { get; set; } = new();
public MsgPair<GetConfig, GetConfigRes> GetConfig { get; set; } = new();
//站外
5 months ago
public MsgPair<PileEndCharge, PileEndChargeResp> PileEndCharge { get; set; } = new();
public MsgPair<PileChargeRealtime, PileChargeRealtimeResp> PileChargeRealtime { get; set; } = new();
5 months ago
public MsgPair<PileRealtime, PileRealtimeResp> PileRealtime { get; set; } = new();
6 months ago
#endregion
#region basic
private IMqttClient? MqttClient;
private List<IBaseHandler> handlers = new();
private static ushort _incrementId;
private static ushort GetIncrementId()
{
if (_incrementId < 65535)
{
_incrementId += 1;
}
else
{
_incrementId = 1;
}
return _incrementId;
}
public void InitHandler()
{
var list = new List<Type>();
foreach (var reg in AppInfo.Container.ComponentRegistry.Registrations)
foreach (var service in reg.Services)
if (service is TypedService ts)
if (MatchHandlers(ts))
list.Add(ts.ServiceType);
foreach (var type in list)
{
var resolve = AppInfo.Container.Resolve(type);
handlers.Add((IBaseHandler)resolve);
}
}
private bool MatchHandlers(TypedService ts)
{
var interfaces = ts.ServiceType.GetInterfaces();
if (interfaces.Length > 0)
foreach (var type in interfaces)
{
if (type.ToString().Contains("Service.Cloud.Handler"))
{
return true;
}
}
return false;
}
public void Connect()
{
Log.Info($"begin connect cloud {ServerIp}:{ServerPort} with client={ClientId}");
if (MqttClient == null)
{
MqttClient = new MqttFactory().CreateMqttClient();
MqttClient.ConnectedHandler = this;
MqttClient.ApplicationMessageReceivedHandler = this;
MqttClient.DisconnectedHandler = this;
}
try
{
var task = MqttClient.ConnectAsync(BuildOptions());
MqttClientConnectResult result = task.Result;
Log.Info($"connect cloud {result.ResultCode}");
if (result.ResultCode == MqttClientConnectResultCode.Success)
{
Connected = true;
}
else
{
Connected = false;
if (AutoReConnect)
{
6 months ago
Thread.Sleep(5000);
Connect();
}
}
}
catch (Exception e)
{
Log.Error("connect cloud error", e);
if (AutoReConnect)
{
6 months ago
Thread.Sleep(5000);
Connect();
}
}
}
/// <summary>
/// 连接成功回调
/// </summary>
/// <param name="eventArgs"></param>
/// <returns></returns>
public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
{
await DoSubTopic(SubTopic);
/*SendVehicleCertification(new VehicleCertification()
{
6 months ago
dt = DateTime.Now,
en = 1,
6 months ago
mode =1,
rfid= "LZ5NB6D33RB000438",
ty= 2,
vi ="LZ5NB6D33RB000438"
});*/
6 months ago
// SendSignIn(new SignIn()
// {
// sn = StaticStationInfo.StationNo,
// st = "01",
// ss = StaticStationInfo.StationStatus,
// en = 1,
// cn = 7
// });
}
private async Task DoSubTopic(string topic)
{
List<MqttTopicFilter> list = new List<MqttTopicFilter>();
string[] topics = topic.Split(new char[] { ',' });
foreach (string str in topics)
{
MqttTopicFilter topicFilter = new MqttTopicFilter
{
Topic = str,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
};
list.Add(topicFilter);
}
await MqttClient.SubscribeAsync(list.ToArray());
Log.Info($"subscribe {topic} success ");
}
public void Publish<T>(T data) where T : ICmd
{
if (MqttClient.IsConnected)
{
Model<T> model = new Model<T>
{
Header = new Header()
{
cmd = data.GetCmd(),
cipherFlag = Encrypt,
id = GetIncrementId(),
sid = StationNo,
timeStamp = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000
},
body = data
};
model.dataSign = "";//SignData(model);
var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DateFormatString = "yyyy-MM-dd HH:mm:ss",
NullValueHandling = NullValueHandling.Ignore
};
5 months ago
Log.Info($"send {JsonConvert.SerializeObject(model, settings)}");
var appMsg = new MqttApplicationMessage
{
Topic = PubTopic,
Payload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model, settings)),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Retain = false
};
MqttClient.PublishAsync(appMsg);
}
}
private string SignData<T>(Model<T> model) where T : ICmd
{
var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DateFormatString = "yyyy-MM-dd HH:mm:ss",
NullValueHandling = NullValueHandling.Ignore
};
string body = JsonConvert.SerializeObject(model.body, settings);
return MD5Util.MD5Encrypt32(body + ":" + model.Header.timeStamp + ":" + model.Header.id).ToLower();
}
/// <summary>
/// 消息接收回调
/// </summary>
/// <param name="eventArgs"></param>
/// <returns></returns>
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
var message = eventArgs.ApplicationMessage;
if (message.Topic != SubTopic)
{
return Task.CompletedTask;
}
if (message.Payload == null)
{
return Task.CompletedTask;
}
string s = Encoding.UTF8.GetString(message.Payload);
if (string.IsNullOrWhiteSpace(s))
{
return Task.CompletedTask;
}
Log.Info($"from cloud receive {s} ");
JObject objResult = JObject.Parse(s);
string headerStr = objResult["header"].ToString();
Header? header = JsonConvert.DeserializeObject<Header>(headerStr);
if (header == null)
{
return Task.CompletedTask;
}
foreach (IBaseHandler handler in handlers)
{
if (handler.CanHandle(header.cmd))
{
string bodyStr = objResult["body"].ToString();
handler.Handle(bodyStr);
break;
}
}
return Task.CompletedTask;
}
/// <summary>
/// 断开回调
/// </summary>
/// <param name="eventArgs"></param>
/// <returns></returns>
public Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs)
{
Log.Info("cloud disconnect");
Connected = false;
if (AutoReConnect)
{
Connect();
}
return Task.CompletedTask;
}
6 months ago
private IMqttClientOptions BuildOptions()
6 months ago
{
MqttClientOptionsBuilder builder =
new MqttClientOptionsBuilder().WithTcpServer(ServerIp, ServerPort).WithClientId(ClientId);
if (!string.IsNullOrWhiteSpace(Username))
{
builder.WithCredentials(Username, Encoding.UTF8.GetBytes(Password));
}
if (IsCleanSession)
{
builder.WithCleanSession();
}
builder.WithKeepAlivePeriod(TimeSpan.FromSeconds(KeepalivePeriod))
.WithCommunicationTimeout(TimeSpan.FromSeconds(Timeout));
switch (MqttVersion)
6 months ago
{
case "3.1.0":
builder.WithProtocolVersion(MqttProtocolVersion.V310);
break;
case "5.0.16":
builder.WithProtocolVersion(MqttProtocolVersion.V500);
break;
default:
builder.WithProtocolVersion(MqttProtocolVersion.V311);
break;
}
return builder.Build();
}
#endregion
#region 主动发送CMD
public CarAuthRes? SendVehicleCertification(CarAuth carAuth,
TimeSpan? timeSpan = null)
{
Log.Info(carAuth);
this.CarAuth.Req = carAuth;
this.Publish(carAuth);
return CarAuth.GetResp(timeSpan);
}
public BatDataInfoRes? SendBatDataInfo(BatDataInfo batDataInfo,
TimeSpan? timeSpan = null)
{
this.BatData.Req = batDataInfo;
this.Publish(batDataInfo);
return BatData.GetResp(timeSpan);
}
/// <summary>
/// 换电站通道状态上报
/// </summary>
/// <param name="stationChnRunStatus"></param>
/// <param name="timeSpan"></param>
/// <returns></returns>
public StationChnRunStatusRes? SendChannelStatusReporting(StationChnRunStatus stationChnRunStatus,
TimeSpan timeSpan)
{
this.ChannelStatus.Req = stationChnRunStatus;
this.Publish(stationChnRunStatus);
return ChannelStatus.GetResp(timeSpan);
}
public AcDataInfoRes? SendAirConditioningData(AcDataInfo acDataInfo,
TimeSpan timeSpan)
{
this.AirConditioning.Req = acDataInfo;
this.Publish(acDataInfo);
return AirConditioning.GetResp(timeSpan);
}
public ChargeRecordReportRes? SendChargeRecordReporting(ChargeRecordReport chargeRecord,
TimeSpan timeSpan)
{
this.ChargeRecord.Req = chargeRecord;
this.Publish(chargeRecord);
return ChargeRecord.GetResp(timeSpan);
}
public EqmStateEndLogInfoRes? SendEndLogMessage(EqmStateEndLogInfo endLogMessage,
TimeSpan timeSpan)
{
this.EndLog.Req = endLogMessage;
this.Publish(endLogMessage);
return EndLog.GetResp(timeSpan);
}
public StationRunStatusRes? SendHostStatusReported(StationRunStatus hostStatusReported,
TimeSpan timeSpan)
{
this.HostStatus.Req = hostStatusReported;
this.Publish(hostStatusReported);
return HostStatus.GetResp(timeSpan);
}
public FaultReportRes? SendRealTimeFaultInfo(FaultReport faultReport,
TimeSpan timeSpan)
{
this.RealTimeFault.Req = faultReport;
this.Publish(faultReport);
return RealTimeFault.GetResp(timeSpan);
}
public DevListRes? SendReportingDeviceList(DevList devList,
TimeSpan timeSpan)
{
this.ReportingDevice.Req = devList;
this.Publish(devList);
return ReportingDevice.GetResp(timeSpan);
}
public SignInRes? SendSignIn(SignIn signIn,
TimeSpan? timeSpan=null)
{
this.Sign.Req = signIn;
this.Publish(signIn);
return Sign.GetResp(timeSpan);
}
public EqmStateStartLogInfoRes? SendStartLogMessage(EqmStateStartLogInfo eqmStateStartLogInfo,
TimeSpan timeSpan)
{
this.StartLog.Req = eqmStateStartLogInfo;
this.Publish(eqmStateStartLogInfo);
return StartLog.GetResp(timeSpan);
}
public EvmDataInfoRes? SendTemperatureHumidityData(EvmDataInfo evmDataInfo,
TimeSpan timeSpan)
{
this.TemperatureHumidity.Req = evmDataInfo;
this.Publish(evmDataInfo);
return TemperatureHumidity.GetResp(timeSpan);
}
/// <summary>
/// 上传换电订单
/// </summary>
/// <param name="staSwapRecord"></param>
/// <param name="timeSpan"></param>
/// <returns></returns>
public StaSwapRecordRes? SendUploadPowerChangeOrder(StaSwapRecord staSwapRecord, TimeSpan? timeSpan = null)
{
this.UploadPowerChange.Req = staSwapRecord;
this.Publish(staSwapRecord);
return UploadPowerChange.GetResp(timeSpan);
}
public CardataReportRes? SendVehicleDataReporting(CardataReport cardataReport,
TimeSpan timeSpan)
{
this.VehicleData.Req = cardataReport;
this.Publish(cardataReport);
return VehicleData.GetResp(timeSpan);
}
6 months ago
public ChargeRecordUploadRes? SendChargeRecordUpLoad(ChargeRecordUpLoad req, TimeSpan? timeSpan = null)
{
this.ChargeRecordUpLoad.Req = req;
this.Publish(req);
return ChargeRecordUpLoad.GetResp(timeSpan);
}
6 months ago
public ChargeDevDataInfoRes? SendChargeDevDataInfo(ChargeDevDataInfo req, TimeSpan? timeSpan = null)
{
this.ChargeDevDataInfo.Req = req;
this.Publish(req);
return ChargeDevDataInfo.GetResp(timeSpan);
}
5 months ago
public BatteryTotalRes? SendBatteryTotal(BatteryTotal req, TimeSpan? timeSpan = null)
{
this.BatteryTotal.Req = req;
this.Publish(req);
return BatteryTotal.GetResp(timeSpan);
}
public ChargingTotalDisRes? SendChargingTotalDis(ChargingTotalDis req, TimeSpan? timeSpan = null)
{
this.ChargingTotalDis.Req = req;
this.Publish(req);
return ChargingTotalDis.GetResp(timeSpan);
}
public PowerTotalRes? SendPowerTotal(PowerTotal req, TimeSpan? timeSpan = null)
{
this.PowerTotal.Req = req;
this.Publish(req);
return PowerTotal.GetResp(timeSpan);
}
public StaChargingTotalRes? SendStaChargingTotal(StaChargingTotal req, TimeSpan? timeSpan = null)
{
this.StaChargingTotal.Req = req;
this.Publish(req);
return StaChargingTotal.GetResp(timeSpan);
}
public MeterEnergyKwhRes? SendMeterEnergyKwh(MeterEnergyKwh req, TimeSpan? timeSpan = null)
{
this.MeterEnergyKwh.Req = req;
this.Publish(req);
return MeterEnergyKwh.GetResp(timeSpan);
}
public MeterDayEnergyValRes? SendMeterDayEnergyVal(MeterDayEnergyVal req, TimeSpan? timeSpan = null)
{
this.MeterDayEnergyVal.Req = req;
this.Publish(req);
return MeterDayEnergyVal.GetResp(timeSpan);
}
public StaHourEnergyValRes? SendStaHourEnergyVal(StaHourEnergyVal req, TimeSpan? timeSpan = null)
{
this.StaHourEnergyVal.Req = req;
this.Publish(req);
return StaHourEnergyVal.GetResp(timeSpan);
}
public StaDayEnergyValRes? SendStaDayEnergyVal(StaDayEnergyVal req, TimeSpan? timeSpan = null)
{
this.StaDayEnergyVal.Req = req;
this.Publish(req);
return StaDayEnergyVal.GetResp(timeSpan);
}
public StaDayOpeEnergyValRes? SendStaDayOpeEnergyVal(StaDayOpeEnergyVal req, TimeSpan? timeSpan = null)
{
this.StaDayOpeEnergyVal.Req = req;
this.Publish(req);
return StaDayOpeEnergyVal.GetResp(timeSpan);
}
public StaHourAmountValRes? SendStaHourAmountVal(StaHourAmountVal req, TimeSpan? timeSpan = null)
{
this.StaHourAmountVal.Req = req;
this.Publish(req);
return StaHourAmountVal.GetResp(timeSpan);
}
5 months ago
public PileEndChargeResp? SendPileEndCharge(PileEndCharge req, TimeSpan? timeSpan = null)
{
this.PileEndCharge.Req = req;
this.Publish(req);
return PileEndCharge.GetResp(timeSpan);
}
5 months ago
5 months ago
public PileChargeRealtimeResp? SendPileChargeRealtime(PileChargeRealtime req, TimeSpan? timeSpan = null)
{
this.PileChargeRealtime.Req = req;
this.Publish(req);
return PileChargeRealtime.GetResp(timeSpan);
}
5 months ago
5 months ago
public PileRealtimeResp? SendPileRealtime(PileRealtime req, TimeSpan? timeSpan = null)
{
this.PileRealtime.Req = req;
this.Publish(req);
return PileRealtime.GetResp(timeSpan);
}
6 months ago
#endregion
#region business func
/// <summary>
///
6 months ago
/// </summary>
/// <param name="chargeOrder"></param>
/// <param name="op">1 自动; 2 人工手动</param>
/// <param name="timeSpan">超时等待</param>
/// <returns></returns>
public bool PublishChargeOrder(List<ChargeOrder> orders, int op, TimeSpan? timeSpan = null)
6 months ago
{
ChargeOrder chargeOrder = orders[0];
ChargeOrder lastChargeOrder = orders[^1];
6 months ago
string swapOrderSn = chargeOrder.SwapOrderSn;
SwapOrder? swapOrder = SwapOrderRepository.QueryByClause(it => it.Sn == swapOrderSn);
ChargeRecordUpLoad req = new ChargeRecordUpLoad()
{
chrsn = chargeOrder.CloudChargeOrder,
son = swapOrder?.CloudSn,
6 months ago
bid = chargeOrder.BatteryNo,
st = chargeOrder.StartTime ?? DateTime.Now,
et = lastChargeOrder.EndTime ?? DateTime.Now,
6 months ago
ssoc = chargeOrder.StartSoc ?? 0,
esoc = lastChargeOrder.StopSoc ?? 0,
6 months ago
//ssoe = chargeOrder.soe
//esoe
dcce =0,
acce =0,
tp = 0,
pp = 0,
fp = 0,
vp = 0,
ct = 0,
cn = orders.Count,
6 months ago
sfs = op,
vin = swapOrder?.VehicleVin,
sfoc = 0,
6 months ago
};
foreach (ChargeOrder order in orders)
{
req.dcce += Convert.ToSingle(order.StopDcElec ?? 0 - order.StartDcElec ?? 0);
req.acce += Convert.ToSingle(order.StopAcElec ?? 0 - order.StartAcElec ?? 0);
req.tp += Convert.ToSingle(order.SharpElecCount);
req.pp += Convert.ToSingle(order.PeakElecCount);
req.fp += Convert.ToSingle(order.FlatElecCount);
req.vp += Convert.ToSingle(order.ValleyElecCount);
req.ct += ((order.EndTime ?? DateTime.Now).Subtract(order.StartTime ?? DateTime.Now)).Minutes;
}
6 months ago
this.SendChargeRecordUpLoad(req);
return true;
}
#endregion
}