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.
414 lines
19 KiB
414 lines
19 KiB
using AutoMapper;
|
|
using Kean.Domain.Task.Commands;
|
|
using Kean.Domain.Task.Enums;
|
|
using Kean.Domain.Task.Events;
|
|
using Kean.Domain.Task.Models;
|
|
using Kean.Domain.Task.Repositories;
|
|
using Kean.Domain.Task.SharedServices.Proxies;
|
|
using Kean.Domain.Task.Strategies;
|
|
using Kean.Infrastructure.Configuration;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
|
|
namespace Kean.Domain.Task.CommandHandlers
|
|
{
|
|
/// <summary>
|
|
/// 创建上架命令处理程序
|
|
/// </summary>
|
|
public sealed class InfeedCommandHandler : CommandHandler<InfeedCommand>
|
|
{
|
|
private readonly ICommandBus _commandBus; // 命令总线
|
|
private readonly INotification _notifications; // 总线通知
|
|
private readonly IMapper _mapper; // 模型映射
|
|
private readonly ITaskRepository _taskRepository; // 任务仓库
|
|
private readonly IWarehouseRepository _warehouseRepository; // 库房仓库
|
|
private readonly BarcodeInterpreter _barcodeInterpreter; // 条码解释器
|
|
private readonly BarcodeValidator _barcodeValidator; // 条码验证器
|
|
private readonly StockProxy _stockProxy; // 仓储域代理
|
|
private readonly InfeedContext<InfeedStrategy_Simple> _infeedContext; // 上架策略
|
|
|
|
private readonly MaterialProxy _materialProxy;
|
|
|
|
/// <summary>
|
|
/// 依赖注入
|
|
/// </summary>
|
|
public InfeedCommandHandler(
|
|
IServiceProvider serviceProvider,
|
|
ICommandBus commandBus,
|
|
INotification notifications,
|
|
IMapper mapper,
|
|
ITaskRepository taskRepository,
|
|
IWarehouseRepository warehouseRepository,
|
|
BarcodeInterpreter barcodeInterpreter,
|
|
BarcodeValidator barcodeValidator,
|
|
StockProxy stockProxy,
|
|
MaterialProxy materialProxy)
|
|
{
|
|
_commandBus = commandBus;
|
|
_notifications = notifications;
|
|
_mapper = mapper;
|
|
_taskRepository = taskRepository;
|
|
_warehouseRepository = warehouseRepository;
|
|
_barcodeInterpreter = barcodeInterpreter;
|
|
_barcodeValidator = barcodeValidator;
|
|
_stockProxy = stockProxy;
|
|
_materialProxy = materialProxy;
|
|
_infeedContext = new(serviceProvider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 处理程序
|
|
/// </summary>
|
|
public override async System.Threading.Tasks.Task Handle(InfeedCommand command, CancellationToken cancellationToken)
|
|
{
|
|
if (command.ValidationResult.IsValid)
|
|
{
|
|
// 电气异常报警(检尺异常)
|
|
if (!string.IsNullOrEmpty(command.Parameters) && command.Parameters.StartsWith("errorMessage:"))
|
|
{
|
|
await _commandBus.Notify(null, $"{command.Parameters[13..]}", null,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
// 条码格式校验
|
|
if (!await _barcodeValidator.Validate(command.Barcode))
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), "条码格式不正确", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
// 任务重复
|
|
if (await _taskRepository.HasTask(command.Barcode))
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), "托盘正在执行任务", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
var pallet = await _barcodeInterpreter.Interpret(command.Barcode);
|
|
|
|
// 获取操作位置
|
|
var original = command.Original switch
|
|
{
|
|
int i => await _warehouseRepository.GetStationById(i),
|
|
long i => await _warehouseRepository.GetStationById((int)i),
|
|
string s when s.StartsWith("name:") => await _warehouseRepository.GetStationByName(s[5..], command.Warehouse),
|
|
string s when s.StartsWith("device:") => await _warehouseRepository.GetStationByDevice(s[7..], command.Warehouse),
|
|
Station s => s,
|
|
_ => null
|
|
};
|
|
// 操作位置可用性
|
|
if (original?.Warehouse != command.Warehouse
|
|
|| original.AllowIn != true
|
|
|| original.Pallet?.Contains(pallet) == false
|
|
|| original.Spec?.Adaptive(command.Spec) == false)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Original), "操作位置不正确", command.Original,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
//校验 托盘规则与申请位置是否匹配
|
|
Models.Warehouse warehouse = await _warehouseRepository.GetWarehouse(original.Warehouse);
|
|
if (!warehouse.Code.Contains(command.Barcode.Substring(2, 1)))
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})不能进入({warehouse.Name})库房", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
if (!(await _stockProxy.GetCell(command.Barcode)).HasValue)
|
|
{
|
|
//@=@ 固定值
|
|
if (command.Tag == "3")
|
|
{
|
|
int? endDevice = original?.Id;
|
|
var sGoods = await _materialProxy.GetMaterial(null, (string)pallet, true);
|
|
Models.Material goods = JsonConvert.DeserializeObject<Models.Material>(sGoods);
|
|
if (goods == null)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Original), "空托盘条码解析类型失败", command.Original,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
int? goodsid = goods.Id;
|
|
if (goodsid.HasValue)
|
|
{
|
|
command.Material = goodsid.Value;
|
|
}
|
|
|
|
await _stockProxy.PalletIn(
|
|
command.Barcode,
|
|
endDevice.Value,
|
|
goodsid.Value,
|
|
command.Operator,
|
|
command.Tag
|
|
);
|
|
}
|
|
else
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), "托盘没有组盘入库,请先组盘", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
command.Material = await _stockProxy.GetStorageMaterial(command.Barcode);
|
|
bool bPallet = await _taskRepository.IsPallet(command.Material.Value);
|
|
|
|
//@=@ 固定值
|
|
if (command.Tag == "3")
|
|
{
|
|
//if (command.Material == 5 || command.Material == 9 || command.Material == 57 || command.Material == 58 || command.Material == 60 || command.Material == 61 || command.Material == 62)
|
|
if (bPallet)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})已经有库存,无法空托盘入库", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//command.Material = await _stockProxy.GetStorageMaterial(command.Barcode);
|
|
}
|
|
|
|
//称重校验
|
|
if (command.Barcode.Substring(2,1) != "F" && command.Barcode.Substring(2, 1) != "G")
|
|
{
|
|
var sGoods = await _materialProxy.GetMaterial(command.Material, null, false);
|
|
Models.Material goods = JsonConvert.DeserializeObject<Models.Material>(sGoods);
|
|
|
|
if (bPallet)
|
|
{
|
|
try
|
|
{
|
|
if (Math.Abs(decimal.Parse(command.Parameters) - goods.Weight.Value) / goods.Weight >= (decimal)0.1)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters})与规定托盘重量({goods.Weight})不匹配", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters}),规定托盘重量({goods.Weight})参数有异常", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var sStockGoods = await _materialProxy.GetMaterial(null, goods.Model, false);
|
|
Models.Material stockGoods = JsonConvert.DeserializeObject<Models.Material>(sStockGoods);
|
|
|
|
IEnumerable<StockLine> stockLines = await _taskRepository.GetLines(command.Barcode);
|
|
|
|
string inputWeight = command.Parameters == null ? "0" : command.Parameters;
|
|
//减去托盘重量
|
|
decimal remainingWeight = decimal.Parse(inputWeight) - stockGoods.Weight.Value;
|
|
decimal unitWeight = 0;
|
|
foreach (var m in stockLines)
|
|
{
|
|
var sMGoods = await _materialProxy.GetMaterial(m.Material, null, false);
|
|
Models.Material mGoods = JsonConvert.DeserializeObject<Models.Material>(sMGoods);
|
|
|
|
remainingWeight = remainingWeight - mGoods.Weight.Value * m.Quantity;
|
|
if (unitWeight == 0)
|
|
{
|
|
unitWeight = mGoods.Weight.Value;
|
|
}
|
|
else if (m.Weight.Value < unitWeight)
|
|
{
|
|
unitWeight = mGoods.Weight.Value;
|
|
}
|
|
}
|
|
|
|
if (Math.Abs(remainingWeight) > unitWeight)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters})与规定物料重量({goods.Weight})不匹配", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters}),规定物料重量({goods.Weight})参数有异常", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!command.Material.HasValue)
|
|
{
|
|
|
|
}
|
|
|
|
// 不允许操作立库中的库存
|
|
var cell = await _stockProxy.GetCell(command.Barcode);
|
|
if (cell.HasValue && await _warehouseRepository.IsCell(cell.Value) == true)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Barcode), "不允许操作立库中的库存", command.Barcode,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
//获取库存信息
|
|
string storageTag = await _taskRepository.GetBypassTag(command.Barcode);
|
|
|
|
string requestNo = string.Empty;
|
|
if (!string.IsNullOrEmpty(storageTag))
|
|
{
|
|
try
|
|
{
|
|
JObject jObj = JObject.Parse(storageTag);
|
|
|
|
JToken endCell = jObj.SelectToken("Cell");
|
|
if (endCell != null)
|
|
{
|
|
command.Destination = "device:" + Convert.ToString(jObj["Cell"]);
|
|
}
|
|
|
|
JToken requestNoFlg = jObj.SelectToken("requestNo");
|
|
if (requestNoFlg != null)
|
|
{
|
|
requestNo = Convert.ToString(jObj["requestNo"]);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
}
|
|
|
|
// 获取目标位置
|
|
var destination = command.Destination switch
|
|
{
|
|
int i => await _warehouseRepository.GetCellById(i),
|
|
long i => await _warehouseRepository.GetCellById((int)i),
|
|
string s when s.StartsWith("name:") => await _warehouseRepository.GetCellByName(s[5..], command.Warehouse),
|
|
string s when s.StartsWith("device:") => await _warehouseRepository.GetCellByDevice(s[7..], command.Warehouse),
|
|
Cell c => c,
|
|
_ => null
|
|
};
|
|
// 目标位置校验
|
|
if (destination != null)
|
|
{
|
|
// 目标位置可用性
|
|
if (destination?.Warehouse != command.Warehouse
|
|
|| destination.IsEmpty != true
|
|
|| destination.State != CellState.Enabled
|
|
|| destination.Pallet?.Contains(pallet) == false
|
|
|| destination.Spec?.Adaptive(command.Spec) == false)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Destination), "目标位置不可用", command.Destination,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
// 自动分配目标位置
|
|
else
|
|
{
|
|
if (command.Manual)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Destination), "未指定目标位置", command.Destination,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
destination = await _infeedContext.AutoDestination(pallet, original, command.Spec, command.Material);
|
|
if (destination == null)
|
|
{
|
|
await _commandBus.Notify(nameof(command.Destination), "设备故障,请先排除故障", command.Destination,
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//车架时
|
|
if (command.Barcode.Substring(2, 1) == "F")
|
|
{
|
|
string originalStockBarcode = command.Barcode;
|
|
command.Barcode = "LCF" + System.DateTime.Now.ToString("yyyy-MM-ddHH:mm:ss.fff").Replace("-", "").Replace(":", "").Replace(".", "");
|
|
|
|
await _taskRepository.ReSetStockBarcode(originalStockBarcode, command.Barcode);
|
|
}
|
|
|
|
|
|
// 时间戳
|
|
command.Timestamp ??= DateTime.Now;
|
|
// 切点事件
|
|
var event0 = _mapper.Map<InfeedExecutingEvent>(command);
|
|
event0.Original = original;
|
|
event0.Destination = destination;
|
|
await _commandBus.Trigger(event0, cancellationToken);
|
|
if (!_notifications.Any())
|
|
{
|
|
// 锁定目标位置
|
|
await _warehouseRepository.LockCell(destination.Id);
|
|
// 非手动模式下的双深避让处理
|
|
if (!command.Manual)
|
|
{
|
|
var avert = new AvertCommand
|
|
{
|
|
Cell = destination,
|
|
Behavior = AvertCommand.Entering,
|
|
Priority = command.Priority,
|
|
Previous = command.Previous,
|
|
Timestamp = command.Timestamp
|
|
};
|
|
await _commandBus.Execute(avert, cancellationToken);
|
|
if (_notifications.Any())
|
|
{
|
|
return;
|
|
}
|
|
if (avert.Id.HasValue)
|
|
{
|
|
command.Previous = avert.Id;
|
|
}
|
|
}
|
|
// 创建任务
|
|
var task = _mapper.Map<Models.Task>(command);
|
|
task.Type = TaskType.Infeed;
|
|
task.Original = original.Id;
|
|
task.Destination = destination.Id;
|
|
task.RequestNo = requestNo;
|
|
|
|
Output(nameof(command.Id), await _taskRepository.CreateTask(task));
|
|
// 成功事件
|
|
var event1 = _mapper.Map<InfeedSuccessEvent>(command);
|
|
event1.Original = original;
|
|
event1.Destination = destination;
|
|
await _commandBus.Trigger(event1, cancellationToken);
|
|
// 手动
|
|
if (command.Manual && !_notifications.Any())
|
|
{
|
|
await _commandBus.Execute(new CompleteCommand { Id = command.Id }, cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await _commandBus.Notify(command.ValidationResult,
|
|
cancellationToken: cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
}
|