|
|
using System.Collections.Concurrent;
|
|
|
using Autofac;
|
|
|
using DotNetty.Common.Utilities;
|
|
|
using Entity.Constant;
|
|
|
using Entity.DbModel.Station;
|
|
|
using HybirdFrameworkCore.Autofac;
|
|
|
using log4net;
|
|
|
using Newtonsoft.Json;
|
|
|
using Repository.Station;
|
|
|
using Service.Execute.Api;
|
|
|
using Service.Execute.Model;
|
|
|
using Service.Execute.Model.Tbox;
|
|
|
using Service.Execute.StaticTools;
|
|
|
using Service.Execute.Step;
|
|
|
using Service.Execute.SwapException;
|
|
|
using Service.Plc.Client;
|
|
|
|
|
|
namespace Service.Execute;
|
|
|
|
|
|
public class SwappingStateMachine : IDisposable
|
|
|
{
|
|
|
private static readonly ILog Log = LogManager.GetLogger(typeof(SwappingStateMachine));
|
|
|
private static readonly SwappingStateMachine SwappingMachine = new SwappingStateMachine();
|
|
|
|
|
|
private BinInfoRepository BinInfoRepository { get; set; }
|
|
|
|
|
|
private SwapAmtOrderRepository AmtOrderRepository { get; set; }
|
|
|
|
|
|
|
|
|
public bool CancelFlag { get; set; } = false;
|
|
|
public bool StopFlag { get; set; } = false;
|
|
|
|
|
|
public bool PlcSwapFlag { get; set; } = false;
|
|
|
|
|
|
//手动确认换电成功
|
|
|
public bool ManualSwapSuccFlag { get; set; } = false;
|
|
|
|
|
|
//手动确认解锁/上锁成功
|
|
|
public bool ManualConfirmCarUnlockFlag { get; set; }
|
|
|
|
|
|
//手动确认解锁/上锁成功
|
|
|
public bool ManualConfirmCarLockFlag { get; set; }
|
|
|
public OperateModel OperateModel = null;
|
|
|
|
|
|
public readonly LedTool? LedTool = null;
|
|
|
private readonly Thread _executeThread;
|
|
|
private readonly Dictionary<SwappingState, IState> _dictionary = new Dictionary<SwappingState, IState>();
|
|
|
|
|
|
private StateResult _result = new StateResult() { SwappingState = SwappingState.Begin };
|
|
|
|
|
|
//给云平台的实时状态
|
|
|
public InfoEnum.BusinessSwappingForCloudState BusinessSwappingForCloudState =
|
|
|
InfoEnum.BusinessSwappingForCloudState.UnKnown;
|
|
|
|
|
|
public DateTime BusinessSwappingStateUpdateTime = DateTime.Now;
|
|
|
public ExceptionReason ExceptionReason = ExceptionReason.None;
|
|
|
|
|
|
//换电实时状态 0:开始 1:成功 2:失败
|
|
|
public int? SwapStatus = null;
|
|
|
public string SwapFailReason = "";
|
|
|
public int StepSort = 0;
|
|
|
|
|
|
public RfidReadModel? RfidReadModel = null;
|
|
|
public TboxCarInfoModel? BoxCarInfoModel = null;
|
|
|
|
|
|
public SwapOrder? SwapOrder = null;
|
|
|
|
|
|
public SwapOrderBatteryInfo? SwapOrderBatteryInfo = null;
|
|
|
|
|
|
public ConcurrentDictionary<string, StepModel> StepModel =
|
|
|
new ConcurrentDictionary<string, StepModel>
|
|
|
{
|
|
|
["0"] = new StepModel { StepNo = 0, Status = 0, StepName = "空闲" },
|
|
|
["1"] = new StepModel { StepNo = 1, Status = 0, StepName = "车辆到站(入口雷达检测到车辆驶入)" },
|
|
|
["2"] = new StepModel { StepNo = 2, Status = 0, StepName = "rfid扫描完成" },
|
|
|
["3"] = new StepModel { StepNo = 3, Status = 0, StepName = "云平台车辆认证" },
|
|
|
["4"] = new StepModel { StepNo = 4, Status = 0, StepName = "车辆到位" },
|
|
|
// ["5"] = new StepModel { StepNo = 5, Status = 0, StepName = "云平台下发换电指令" },
|
|
|
["5"] = new StepModel { StepNo = 5, Status = 0, StepName = "车辆解锁" },
|
|
|
//["6"] = new StepModel { StepNo = 6, Status = 0, StepName = "下发plc选包" },
|
|
|
|
|
|
["6"] = new StepModel { StepNo = 6, Status = 0, StepName = "开始换电" },
|
|
|
["7"] = new StepModel { StepNo = 7, Status = 0, StepName = "拆旧电池完成" },
|
|
|
["8"] = new StepModel { StepNo = 8, Status = 0, StepName = "入库旧电池完成" },
|
|
|
["9"] = new StepModel { StepNo = 9, Status = 0, StepName = "搬运新电池完成" },
|
|
|
["10"] = new StepModel { StepNo = 10, Status = 0, StepName = "安装新电池完成" },
|
|
|
["11"] = new StepModel { StepNo = 11, Status = 0, StepName = "安装完成" },
|
|
|
["12"] = new StepModel { StepNo = 12, Status = 0, StepName = "车辆上锁" },
|
|
|
["13"] = new StepModel { StepNo = 13, Status = 0, StepName = "换电完成(车辆驶离)" },
|
|
|
["14"] = new StepModel { StepNo = 14, Status = 0, StepName = "换电失败(车辆驶离)" },
|
|
|
};
|
|
|
|
|
|
#region 小步状态
|
|
|
|
|
|
//雷达检测/车辆进入
|
|
|
public bool RadarInFlag = false;
|
|
|
|
|
|
//开始读rf
|
|
|
public bool BeginRfidReadFlag = false;
|
|
|
|
|
|
//读rfid
|
|
|
public bool RfidReadFlag = false;
|
|
|
|
|
|
//云平台车辆
|
|
|
public bool CloudVelCheckFlag = false;
|
|
|
|
|
|
//Tbox连接
|
|
|
public bool BoxConnectFlag = false;
|
|
|
|
|
|
public bool CarInPhotoPositionFlag = false;
|
|
|
|
|
|
//车辆lock
|
|
|
public bool BoxLocalCheckFlag = false;
|
|
|
|
|
|
//车辆数据上传
|
|
|
public bool CloudTBoxFlag = false;
|
|
|
|
|
|
//选包
|
|
|
public bool SelectPackFlag = false;
|
|
|
|
|
|
//车辆到位
|
|
|
public bool VehiclesInPlaceFlag = false;
|
|
|
|
|
|
//下发选包
|
|
|
public bool DistributeSelectPackFlag = false;
|
|
|
|
|
|
//云平台下发
|
|
|
public bool CloudCarCanStartFlag = false;
|
|
|
|
|
|
//车辆解锁
|
|
|
public bool VelUnlockFlag = false;
|
|
|
|
|
|
//开始换电
|
|
|
public bool StartSwappingFlag = false;
|
|
|
|
|
|
|
|
|
//上使能
|
|
|
public bool PlcHoldFlag = false;
|
|
|
|
|
|
//拍照状态
|
|
|
public bool ChannelStatusOkFlag = false;
|
|
|
|
|
|
//二次检测车
|
|
|
public bool VehiclesInPlace2Flag = false;
|
|
|
|
|
|
//拆旧电池
|
|
|
public bool UnOldBatteryFlag = false;
|
|
|
|
|
|
//入库旧电池
|
|
|
public bool StorageOldBatteryFlag = false;
|
|
|
|
|
|
//搬运新电池
|
|
|
public bool OutNewBatteryFlag = false;
|
|
|
|
|
|
//安装新电池
|
|
|
public bool InstallNewBatteryFlag = false;
|
|
|
|
|
|
//安装完成
|
|
|
public bool FinishNewBatteryFlag = false;
|
|
|
|
|
|
//航车回归安全位置
|
|
|
public bool ToSafePositionFlag = false;
|
|
|
|
|
|
//车辆上锁
|
|
|
public bool VelLockFlag = false;
|
|
|
|
|
|
|
|
|
//车辆离开
|
|
|
public bool RadarOutFlag = false;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
private SwappingStateMachine()
|
|
|
{
|
|
|
_executeThread = new Thread(Work);
|
|
|
_executeThread.Name = "Swapping main";
|
|
|
|
|
|
_dictionary[SwappingState.Begin] = new BeginState();
|
|
|
_dictionary[SwappingState.StationReady] = new StationReadyState();
|
|
|
_dictionary[SwappingState.CarPrepare] = new CarPrepareState();
|
|
|
_dictionary[SwappingState.SwapCanStart] = new SwapCanStartState();
|
|
|
_dictionary[SwappingState.CarCtrl] = new CarCtrlState();
|
|
|
_dictionary[SwappingState.DoSwapping] = new DoSwappingState();
|
|
|
_dictionary[SwappingState.SwapDone] = new SwapDoneState();
|
|
|
_dictionary[SwappingState.Exception] = new ExceptionState();
|
|
|
_dictionary[SwappingState.Canceled] = new CancelState();
|
|
|
_dictionary[SwappingState.ManualSucc] = new ManualSuccState();
|
|
|
|
|
|
LedTool = new LedTool();
|
|
|
|
|
|
BinInfoRepository = AppInfo.Container.Resolve<BinInfoRepository>();
|
|
|
AmtOrderRepository = AppInfo.Container.Resolve<SwapAmtOrderRepository>();
|
|
|
}
|
|
|
|
|
|
public static SwappingStateMachine GetInstance()
|
|
|
{
|
|
|
return SwappingMachine;
|
|
|
}
|
|
|
|
|
|
|
|
|
public static StateResult ReturnWithInvokeErr(InvokeStatus status, ExceptionReason exceptionReason)
|
|
|
{
|
|
|
if (status == InvokeStatus.Cancel)
|
|
|
{
|
|
|
return new StateResult() { SwappingState = SwappingState.Canceled };
|
|
|
}
|
|
|
else if (status == InvokeStatus.TimeOut)
|
|
|
{
|
|
|
return null;
|
|
|
}
|
|
|
else if (status == InvokeStatus.ManualSucc)
|
|
|
{
|
|
|
return new StateResult() { SwappingState = SwappingState.ManualSucc };
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return StateResult.Exception(exceptionReason, null);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Work()
|
|
|
{
|
|
|
Log.Info("State machine begin work");
|
|
|
while (!StopFlag)
|
|
|
{
|
|
|
Log.Info($"handle = {_result.SwappingState} begin");
|
|
|
try
|
|
|
{
|
|
|
StateResult? result = _dictionary[_result.SwappingState].Handle(this);
|
|
|
if (result != null)
|
|
|
{
|
|
|
_result = result;
|
|
|
}
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
|
Log.Error($"handle {_result.SwappingState} error", e);
|
|
|
}
|
|
|
|
|
|
Log.Info($"handle = {_result.SwappingState} end");
|
|
|
Thread.Sleep(500);
|
|
|
}
|
|
|
|
|
|
Log.Info("State machine canceled");
|
|
|
}
|
|
|
|
|
|
public void Stop()
|
|
|
{
|
|
|
StopFlag = true;
|
|
|
CancelFlag = true;
|
|
|
Thread.Sleep(2000);
|
|
|
}
|
|
|
|
|
|
public bool IsCanceled()
|
|
|
{
|
|
|
return CancelFlag;
|
|
|
}
|
|
|
|
|
|
public bool IsManualSwapSucc()
|
|
|
{
|
|
|
return ManualSwapSuccFlag;
|
|
|
}
|
|
|
|
|
|
public bool Start()
|
|
|
{
|
|
|
Reset();
|
|
|
_executeThread.Start();
|
|
|
Log.Info($"start machine ok");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public void Reset()
|
|
|
{
|
|
|
Log.Info("reset data");
|
|
|
if (PlcMgr.PlcClient?.ReadTaskNo() == 1)
|
|
|
{
|
|
|
PlcMgr.PlcClient?.ExChangeTaskNo(0);
|
|
|
}
|
|
|
|
|
|
ResetOrderAmtStatus();
|
|
|
if (RfidReadModel != null)
|
|
|
{
|
|
|
//wifi数据重置
|
|
|
TBoxApi.Reset(RfidReadModel.VelVin);
|
|
|
}
|
|
|
|
|
|
|
|
|
//重置所有小步状态
|
|
|
ResetStep();
|
|
|
ResetData();
|
|
|
|
|
|
//重置云平台下发的指令
|
|
|
// CloudApi.ClearCarCanStartInfo();
|
|
|
}
|
|
|
|
|
|
public bool Cancel()
|
|
|
{
|
|
|
CancelFlag = true;
|
|
|
Thread.Sleep(5000);
|
|
|
Log.Info($"Cancel machine ok");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public bool Manual()
|
|
|
{
|
|
|
ManualSwapSuccFlag = true;
|
|
|
Thread.Sleep(5000);
|
|
|
Log.Info($"ManualSwapSuccFlag machine ok");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public bool ManualConfirmLockCar()
|
|
|
{
|
|
|
ManualConfirmCarLockFlag = true;
|
|
|
Thread.Sleep(2000);
|
|
|
Log.Info($"ManualConfirmLockCar ok");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public bool ManualConfirmUnLockCar()
|
|
|
{
|
|
|
ManualConfirmCarUnlockFlag = true;
|
|
|
Thread.Sleep(2000);
|
|
|
Log.Info($"ManualConfirmLockCar ok");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
private void ResetData()
|
|
|
{
|
|
|
BusinessSwappingForCloudState = InfoEnum.BusinessSwappingForCloudState.Idle;
|
|
|
BusinessSwappingStateUpdateTime = DateTime.Now;
|
|
|
ExceptionReason = ExceptionReason.None;
|
|
|
OperateModel = null;
|
|
|
;
|
|
|
|
|
|
//换电实时状态 0:开始 1:成功 2:失败
|
|
|
SwapStatus = null;
|
|
|
SwapFailReason = "";
|
|
|
|
|
|
RfidReadModel = null;
|
|
|
BoxCarInfoModel = null;
|
|
|
|
|
|
SwapOrder = null;
|
|
|
|
|
|
SwapOrderBatteryInfo = null;
|
|
|
StepSort = 0;
|
|
|
|
|
|
foreach (var key in StepModel.Keys.ToList())
|
|
|
{
|
|
|
StepModel[key].Status = 0;
|
|
|
StepModel[key].StartTime = null;
|
|
|
}
|
|
|
|
|
|
PlcSwapFlag = false;
|
|
|
ManualSwapSuccFlag = false;
|
|
|
OperateModel = null;
|
|
|
ManualConfirmCarUnlockFlag = false;
|
|
|
ManualConfirmCarLockFlag = false;
|
|
|
CancelFlag = false;
|
|
|
StopFlag = false;
|
|
|
}
|
|
|
|
|
|
private void ResetStep()
|
|
|
{
|
|
|
RadarInFlag = false;
|
|
|
|
|
|
|
|
|
BeginRfidReadFlag = false;
|
|
|
|
|
|
|
|
|
RfidReadFlag = false;
|
|
|
|
|
|
|
|
|
CloudVelCheckFlag = false;
|
|
|
|
|
|
|
|
|
BoxConnectFlag = false;
|
|
|
CarInPhotoPositionFlag = false;
|
|
|
|
|
|
BoxLocalCheckFlag = false;
|
|
|
|
|
|
|
|
|
CloudTBoxFlag = false;
|
|
|
|
|
|
|
|
|
SelectPackFlag = false;
|
|
|
|
|
|
|
|
|
VehiclesInPlaceFlag = false;
|
|
|
|
|
|
|
|
|
DistributeSelectPackFlag = false;
|
|
|
|
|
|
|
|
|
CloudCarCanStartFlag = false;
|
|
|
|
|
|
|
|
|
VelUnlockFlag = false;
|
|
|
|
|
|
|
|
|
StartSwappingFlag = false;
|
|
|
|
|
|
|
|
|
PlcHoldFlag = false;
|
|
|
|
|
|
|
|
|
ChannelStatusOkFlag = false;
|
|
|
|
|
|
|
|
|
VehiclesInPlace2Flag = false;
|
|
|
|
|
|
|
|
|
UnOldBatteryFlag = false;
|
|
|
|
|
|
|
|
|
StorageOldBatteryFlag = false;
|
|
|
|
|
|
|
|
|
OutNewBatteryFlag = false;
|
|
|
|
|
|
|
|
|
InstallNewBatteryFlag = false;
|
|
|
|
|
|
|
|
|
FinishNewBatteryFlag = false;
|
|
|
|
|
|
|
|
|
ToSafePositionFlag = false;
|
|
|
|
|
|
|
|
|
VelLockFlag = false;
|
|
|
|
|
|
|
|
|
RadarOutFlag = false;
|
|
|
}
|
|
|
|
|
|
//关于仓库锁定相关重置
|
|
|
private void ResetOrderAmtStatus()
|
|
|
{
|
|
|
//仓库解锁
|
|
|
if (SwapOrderBatteryInfo != null)
|
|
|
{
|
|
|
if (SwapOrderBatteryInfo?.UpBinInfo != null) ;
|
|
|
{
|
|
|
BinInfoRepository.Update(i => i.AmtLock == (int)InfoEnum.AmtBatLockStatus.UnLock,
|
|
|
i => i.Id == SwapOrderBatteryInfo.UpBinInfo.Id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//预约单
|
|
|
List<SwapAmtOrder> amtOrderInfos = AmtOrderRepository.QueryListByClause(it =>
|
|
|
it.ExpireTime < DateTime.Now
|
|
|
&& it.Status == (byte)InfoEnum.AmtOrderStatus.Success).ToList();
|
|
|
if (amtOrderInfos.Count > 0)
|
|
|
{
|
|
|
List<string> binNos = amtOrderInfos.SelectMany(i => i.AmtBinNoList.Split(',').ToList()).Distinct().ToList();
|
|
|
List<BinInfo> listByClause = BinInfoRepository.QueryListByClause(i => binNos.Contains(i.No));
|
|
|
foreach (var binInfo in listByClause)
|
|
|
{
|
|
|
binInfo.AmtLock = (int)InfoEnum.AmtBatLockStatus.UnLock;
|
|
|
}
|
|
|
|
|
|
BinInfoRepository.Update(listByClause);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void Dispose()
|
|
|
{
|
|
|
StopFlag = true;
|
|
|
CancelFlag = true;
|
|
|
Thread.Sleep(2000);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public enum SwappingState
|
|
|
{
|
|
|
Begin,
|
|
|
StationReady,
|
|
|
CarPrepare,
|
|
|
SwapCanStart,
|
|
|
CarCtrl,
|
|
|
DoSwapping,
|
|
|
SwapDone,
|
|
|
Exception,
|
|
|
Canceled,
|
|
|
ManualSucc,
|
|
|
} |