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.

473 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Autofac;
using Entity.DbModel.Station;
using HybirdFrameworkCore.Autofac;
using HybirdFrameworkCore.Autofac.Attribute;
using HybirdFrameworkCore.Entity;
using HybirdFrameworkCore.Redis;
using HybirdFrameworkCore.Utils;
using HybirdFrameworkDriver.Session;
using HybirdFrameworkDriver.TcpClient;
using Newtonsoft.Json;
using Repository.Station;
using Service.Charger.Codec;
using Service.Charger.Common;
using Service.Charger.Handler;
using Service.Charger.Msg;
using Service.Charger.Msg.Charger.Req;
using Service.Charger.Msg.Charger.Resp;
using Service.Charger.Msg.Host.Req;
using Service.Charger.Msg.Host.Req.Bms;
using Service.Init;
namespace Service.Charger.Client;
/// <summary>
/// 示例程序
/// </summary>
[Scope("InstancePerDependency")]
public class ChargerClient : TcpClient<IBaseHandler, Decoder, Encoder>
{
#region 属性
public ushort AuthTimes { get; set; } = 0;
public bool IsAuthed { get; set; } = false;
/// <summary>
/// 参考 Service.Charger.Common.ChargingStatus
/// </summary>
public UInt16 ChargingStatus { get; set; }
/// <summary>
/// 是否已经开始充电
/// </summary>
public bool IsCharged { get; set; } = false;
public bool IsStopped { get; set; } = false;
public bool IsCanSendStopCmd { get; set; } = true;
public DateTime? ChargingStartTime { get; set; }
public DateTime? ChargingStopTime { get; set; }
/// <summary>
/// 电池包实时数据
/// </summary>
public BatteryPackData? BatteryPackData { get; set; }
/// <summary>
/// 电池包实时单体温度&单体电压数据
/// </summary>
public BatteryPackDataVoltage? BatteryPackDataVoltage { get; set; }
/// <summary>
/// 电池包上报累计充放电电量
/// </summary>
public BatteryPackTotalElectricity? BatteryPackTotalElectricity { get; set; }
/// <summary>
/// 电池包上报充放电口温度
/// </summary>
public BatteryPackPortTemperature? BatteryPackPortTemperature { get; set; }
/// <summary>
/// 电池包内部接触器状态和故障上报
/// </summary>
public BatteryPackStateAndFault? BatteryPackStateAndFault { get; set; }
/// <summary>
/// 充放电设备应答站功率调节指令
/// </summary>
public PowerRegulationRes PowerRegulationRes { get; set; }
/// <summary>
/// 电池包实时遥信上报(站内充电模式有电池包时周期性上传)
/// </summary>
public RemoteSignaling RemoteSignaling { get; set; }
/// <summary>
/// 充电机上报车辆 VIN
/// </summary>
public VehicleVIN VehicleVIN { get; set; }
/// <summary>
/// 充放电机应答辅助控制
/// </summary>
public AuxiliaryPowerRes AuxiliaryPowerRes { get; set; }
/// <summary>
/// 充放电上报交流电表数据(交流电表接到充电机上的情况)
/// </summary>
public AcMeter AcMeter { get; set; }
/// <summary>
///充电机遥信数据
/// </summary>
public UploadRemoteSignalData UploadRemoteSignalData = new UploadRemoteSignalData();
/// <summary>
/// 充电机工作状态-从遥信数据包中得到。00H待机 01H工作 02H工作完成 03H充/放电暂停
/// </summary>
public byte Workstate { get; set; }
/// <summary>
/// 充电机故障-遥信数据包总故障 00H正常、01H故障
/// </summary>
public bool TotalError { get; set; }
/// <summary>
/// 充电机告警-遥信数据包总告警 00H正常、01H告警
/// </summary>
public bool TotalWarning { get; set; }
/// <summary>
/// 充电机遥测数据
/// </summary>
public UploadTelemetryData UploadTelemetryData = new UploadTelemetryData();
/// <summary>
///充电机实时充电功率
/// </summary>
public float RealTimeChargePower { get; set; } = 0;
/// <summary>
/// 心跳-桩状态
/// </summary>
public byte PileState { get; set; }
/// <summary>
/// 电池编码
/// </summary>
public string? BatteryNo { get; set; }
/// <summary>
/// 电池厂家
/// </summary>
public byte? BatteryFactory { get; set; }
/// <summary>
/// 电池仓编号
/// </summary>
public string? BinNo { get; set; }
/// <summary>
/// 远程升级-监控网关上送升级完成确认帧
/// </summary>
public UplinkUpgrade UplinkUpgrade { get; set; }
/// <summary>
/// 充电订单号
/// </summary>
public string? ChargeOrderNo { get; set; }
/// <summary>
/// 当前指令
/// </summary>
public string? CurrentCmd { get; set; }
/// <summary>
/// 当前接收报文
/// </summary>
public string? CurrentMsg { get; set; }
#endregion
#region db
private ChargeOrderRepository _chargeOrderRepository;
private BinInfoRepository _binInfoRepository;
#endregion
public ChargerClient(ChargeOrderRepository chargeOrderRepository, BinInfoRepository binInfoRepository)
{
_chargeOrderRepository = chargeOrderRepository;
_binInfoRepository = binInfoRepository;
}
/// <summary>
///
/// </summary>
/// <param name="asdu"></param>
public void ReceiveMsgHandle(ASDU asdu)
{
this.CurrentMsg = CurrentCmd = JsonConvert.SerializeObject(asdu, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(asdu.ToBytes());
}
#region 发送指令
private ushort IncreAuthTimes()
{
if (AuthTimes < 65535)
{
AuthTimes += 1;
}
else
{
AuthTimes = 1;
}
return AuthTimes;
}
/// <summary>
/// 发送鉴权
/// </summary>
public void SendAuth()
{
byte authCodeKey = ChargerUtils.GetByteRandomNum(); //鉴码KEY[随机数]
byte[] authCodes = ChargerUtils.GetAuthCodesResult(ChargerConst.AuthCode, authCodeKey); //鉴权码
Auth auth = new Auth(IncreAuthTimes(), authCodes, authCodeKey);
CurrentCmd = JsonConvert.SerializeObject(auth, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(auth.ToBytes());
this.Channel.WriteAndFlushAsync(auth);
}
/// <summary>
/// 监控平台发送远程开始充电指令
/// </summary>
/// <param name="socLimit">SOC限制.百分比</param>
/// <param name="changePowerCmdType">功率调节指令类型.默认1 绝对功率值</param>
/// <param name="changePower">0.1kw/位,默认3600</param>
/// <param name="chargeOrderNo">充电流水号</param>
public string SendRemoteStartCharging(byte socLimit, float changePower = 3600, byte changePowerCmdType = 1,
string? chargeOrderNo = null)
{
if (string.IsNullOrWhiteSpace(chargeOrderNo))
{
chargeOrderNo = ChargerUtils.GenChargeOrderSn();
}
var remoteStartCharging = new RemoteStartCharging(socLimit, changePowerCmdType, changePower, chargeOrderNo);
CurrentCmd = JsonConvert.SerializeObject(remoteStartCharging, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(remoteStartCharging.ToBytes());
this.Channel.WriteAndFlushAsync(remoteStartCharging);
return chargeOrderNo;
}
/// <summary>
/// 监控平台发送远程停止充电指令
/// </summary>
/// <param name="reason">0 正常停机 1 服务器发现桩异常,强制停机</param>
public void SendRemoteStopCharging(byte reason = 0)
{
RemoteStopCharging remoteStopCharging = new RemoteStopCharging(reason);
CurrentCmd = JsonConvert.SerializeObject(remoteStopCharging, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(remoteStopCharging.ToBytes());
this.Channel.WriteAndFlushAsync(remoteStopCharging);
}
/// <summary>
/// 监控平台发送功率调节指令
/// </summary>
/// <param name="expectedOperatingPower">期望运行功率</param>
public void SendPowerRegulation(float expectedOperatingPower)
{
PowerRegulation powerRegulation = new PowerRegulation(expectedOperatingPower);
this.Channel.WriteAndFlushAsync(powerRegulation);
}
/// <summary>
/// 倍率 例如0.单5C位该0值.1C为 5 ,1C 时该值为 10
/// </summary>
/// <param name="rate"></param>
public void SendAdjustChargeRate(float rate)
{
AdjustChargeRate adjustChargeRate = new AdjustChargeRate(rate);
this.Channel.WriteAndFlushAsync(adjustChargeRate);
}
/// <summary>
/// 监控平台下发辅源控制指令
/// </summary>
/// <param name="openFlag">打开辅助电源标志 1电池包辅助电源导通 0电池包辅助电源断开</param>
public void SendAuxiliaryPower(byte openFlag)
{
AuxiliaryPower auxiliaryPower = new AuxiliaryPower(openFlag);
CurrentCmd = JsonConvert.SerializeObject(auxiliaryPower, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(auxiliaryPower.ToBytes());
this.Channel.WriteAndFlushAsync(auxiliaryPower);
}
/// <summary>
/// 监控平台下发电池仓的状态
/// </summary>
/// <param name="battery">是否有电池 0:无电池 1有电池</param>
/// <param name="connectionState">电接头连接状态 0:未连接 1: 已连接</param>
/// <param name="waterCondition">水接头状态 0:未连接 1: 已连接</param>
public void SendBatteryHolderStatus(byte battery, byte connectionState, byte waterCondition)
{
BatteryHolderStatus batteryHolderStatus = new BatteryHolderStatus(battery, connectionState, waterCondition);
CurrentCmd = JsonConvert.SerializeObject(batteryHolderStatus, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(batteryHolderStatus.ToBytes());
this.Channel.WriteAndFlushAsync(batteryHolderStatus);
}
/// <summary>
/// 站控下发 VIN 鉴权的结果
/// </summary>
/// <param name="vinresult">VIN 鉴权结果 1:通过 2 不通过</param>
public void SendAuthenticationVIN(byte vinresult)
{
AuthenticationVIN authenticationVIN = new AuthenticationVIN(vinresult);
this.Channel.WriteAndFlushAsync(authenticationVIN);
}
/// <summary>
/// 远程升级-站级监控升级请求下发
/// </summary>
/// <param name="executionControl">执行控制 0x01立即执行 0x02空闲执行</param>
/// <param name="downloadTimeout">下载超时时间</param>
/// <param name="versionNumber">版本号</param>
/// <param name="fileName">文件名称</param>
/// <param name="fileSize">文件大小</param>
/// <param name="mD5Verification">MD5校验值</param>
/// <param name="url">URL文件路径</param>
public void SendUpgradeRequest(byte executionControl, byte downloadTimeout, string versionNumber,
string fileName, uint fileSize, string mD5Verification, string url)
{
UpgradeRequest upgradeRequest = new UpgradeRequest(executionControl, downloadTimeout, versionNumber, fileName,
fileSize, mD5Verification, url);
this.Channel.WriteAndFlushAsync(upgradeRequest);
}
/// <summary>
/// 设置尖峰平谷时间段
/// </summary>
/// <param name="setPeakValleyTime"></param>
public void SendSetPeakValleyTime(SetPeakValleyTime setPeakValleyTime)
{
CurrentCmd = JsonConvert.SerializeObject(setPeakValleyTime, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(setPeakValleyTime.ToBytes());
this.Channel.WriteAndFlushAsync(setPeakValleyTime);
}
/// <summary>
/// 3.4.7 监控平台下发掉线停止充电
/// </summary>
/// <param name="enabled"> 0不使能 1使能</param>
public void SendOfflineStopCharging(byte enabled)
{
OfflineStopCharging offlineStopCharging = new OfflineStopCharging(enabled);
CurrentCmd = JsonConvert.SerializeObject(offlineStopCharging, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(offlineStopCharging.ToBytes());
this.Channel.WriteAndFlushAsync(offlineStopCharging);
}
/// <summary>
/// 3.4.12 站控设备切换站内/站外充电切换
/// </summary>
/// <param name="chargeMode">00:无效 01:站内 02:站外</param>
public void SendChangeChargeMode(byte chargeMode)
{
ChangeChargeMode req = new ChangeChargeMode(chargeMode);
this.Channel.WriteAndFlushAsync(req);
}
/// <summary>
///
/// </summary>
public void SendQueryBattery()
{
QueryBattery queryBattery = new QueryBattery(ChargerConst.BatteryNo);
CurrentCmd = JsonConvert.SerializeObject(queryBattery, Formatting.Indented) + "\r\n" +
BitUtls.BytesToHexStr(queryBattery.ToBytes());
this.Channel.WriteAndFlushAsync(queryBattery);
}
#endregion
#region 启动充电
/// <summary>
///
/// </summary>
public Result<bool> StartCharge()
{
if (string.IsNullOrWhiteSpace(BinNo))
{
return Result<bool>.Fail("charger init error with no BinNo");
}
if (!Connected)
{
return Result<bool>.Fail($"charger-{BinNo} disconnect");
}
BinInfo binInfo = _binInfoRepository.QueryByClause(it => it.Code == BinNo);
if (binInfo == null)
{
return Result<bool>.Fail($"charger-{BinNo} not exist");
}
BatteryNo = binInfo.BatteryNo;
if (string.IsNullOrWhiteSpace(BatteryNo))
{
return Result<bool>.Fail($"charger-{BinNo} battery not exist");
}
if (binInfo.Status == 0)
{
return Result<bool>.Fail($"仓-{BinNo} 被禁用");
}
RedisHelper redisHelper = AppInfo.Container.Resolve<RedisHelper>();
string? lockKey = redisHelper.GetStrValue($"chargeNo{BinNo}Start");
if (!string.IsNullOrWhiteSpace(lockKey))
{
return Result<bool>.Success($"charger-{BinNo} is starting");
}
redisHelper.SetKeyValueStr($"chargeNo{BinNo}Start", DateTime.Now.ToString("f"), TimeSpan.FromMinutes(1));
byte chargeSoc = StaticStationInfo.ChargeSoc;
string chargeOrderNo = SendRemoteStartCharging(chargeSoc);
_chargeOrderRepository.Insert(new ChargeOrder()
{
Sn = chargeOrderNo,
BatteryNo = BatteryNo,
CmdStatus = 0,
ChargerNo = BinNo,
ChargeMode = 1,
StartMode = 1
});
return Result<bool>.Success(true, "发送成功");
}
#endregion
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool Connect()
{
base.BaseConnect();
if (Connected)
{
SendAuth();
}
return Connected;
}
/// <summary>
///
/// </summary>
/// <param name="sn"></param>
/// <param name="destAddr"></param>
public void SessionAttr(string sn, string destAddr)
{
ChannelUtils.AddAttr(Channel, ChargerConst.ChargerSn, sn);
ChannelUtils.AddAttr(Channel, ChargerConst.EqmTypeNo, sn);
ChannelUtils.AddAttr(Channel, ChargerConst.EqmCode, sn);
ChannelUtils.AddAttr(Channel, ChargerConst.DestAddr, destAddr);
}
}