first commit

This commit is contained in:
2026-09-02 16:31:50 +08:00
commit a461727193
911 changed files with 692450 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
[*.cs]
# CS8618: 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
dotnet_diagnostic.CS8618.severity = none
# CS8600: 将 null 字面量或可能为 null 的值转换为非 null 类型。
dotnet_diagnostic.CS8600.severity = none
# CA1305: 指定 IFormatProvider
dotnet_diagnostic.CA1305.severity = none
+24
View File
@@ -0,0 +1,24 @@
bin/
**/*/bin/
.vs/
Debug/
Release/
Build/
obj/
/ACS/Properties/PublishProfiles/FolderProfile.pubxml
/ACS/Properties/PublishProfiles/FolderProfile.pubxml.user
/JSMachine.PrintACS.Windows/JSMachine.PrintACS.Windows.csproj.user
/JSMachine.PrintACS.Windows/ACS_Logs
ACS_Logs/
/JSMachine.PrintACS.Windows/Properties/PublishProfiles/FolderProfile.pubxml.user
/JSMachine.PrintACS.Windows/Properties/PublishProfiles
/JSMachine.PrintACS.Authorization/tempkey.jwk
/JSMachine.PrintACS.Authorization/tempkey.jwk
PublishProfiles
/JSMachine.PaperMES.Authorization/JSMachine.PaperMES.Authorization.csproj.user
/JSMachine.PaperMES.WebHost/JSMachine.PaperMES.WebHost.csproj.user
/JSMachine.PaperMES.PaperPreparation/JSMachine.PaperMES.PaperPreparation.csproj.user
/JSMachine.PaperMES.PaperReturn/JSMachine.PaperMES.PaperReturn.csproj.user
/JSMachine.PaperMES.PaperPreparation.Modules/FodyWeavers.xml
/JSMachine.PaperMES.PaperPreparation.Modules/FodyWeavers.xsd
/JSMachine.PaperMES.PaperFeed/JSMachine.PaperMES.PaperFeed.csproj.user
@@ -0,0 +1,135 @@
using JSMachine.WMS.App.Dto.Enum;
using Prism.Mvvm;
namespace JSMachine.WMS.App.Dto
{
public partial class AGVTaskDto: BindableBase, IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// ErpTaskId
/// </summary>
public Guid ErpTaskId { get; set; }
/// <summary>
/// 合格证号
/// </summary>
public string BarCode { get; set; }
/// <summary>
/// 订单号
/// </summary>
public string OrderNo { get; set; }
/// <summary>
/// 用户名称
/// </summary>
public string CustomName { get; set; }
/// <summary>
/// 物料名称
/// </summary>
public string MateriaName { get; set; }
/// <summary>
/// 物料类型
/// </summary>
public string MateriaType { get; set; }
/// <summary>
/// 单个托盘上面的物料数量
/// </summary>
public int MateriaNum { get; set; }
/// <summary>
/// 批次号的生成规则为: 作业单号+日期+班次
/// </summary>
public string MateriaBatch { get; set; }
/// <summary>
/// 物料编号
/// </summary>
public string MateriaCode { get; set; }
/// <summary>
/// 联动线出口,机械手设备唯一号
/// </summary>
public string EquipmentNo { get; set; }
/// <summary>
/// 工单状态 1:生成任务 2:开始执行 3:第一次暂停 4:执行第二段 5:第二次暂停 6:执行第三段 7:执行成功 -1:任务取消
/// </summary>
public TaskStatusEnum TaskStatus { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public TaskTypeEnum TaskType { get; set; }
/// <summary>
/// 优先级
/// </summary>
public PriorityEnum Priority { get; set; }
/// <summary>
/// 楼层
/// </summary>
public FloorEnum Floor { get; set; }
/// <summary>
/// 起点(库位编号)
/// </summary>
public string StartPositionCode { get; set; }
/// <summary>
/// 终点(库位编号)
/// </summary>
public string EndPositionCode { get; set; }
/// <summary>
/// 最终出库码头位置(仅对出库任务有效) 1:北区 2:南区 3:人工 4:退货到车间 99:其它
/// </summary>
public TransportEnum Transport { get; set; }
/// <summary>
/// 任务是否锁定(在锁定的情况下agvtask暂时不向rcs发送命令,待解锁后才可以,例如入库二楼提升机出口任务,如果托盘还未到出口则该任务还不能执行)
/// </summary>
public bool IsLock { get; set; }
/// <summary>
/// Erp数据上传状态
/// </summary>
public ErpUploadStatusEnum ErpUploadStatus { get; set; }
/// <summary>
/// Erp返回错误信息
/// </summary>
public string ErpReturnMsg { get; set; }
/// <summary>
/// 重试次数
/// </summary>
public int RetryNum { get; set; }
/// <summary>
/// 分组Id
/// </summary>
public Guid GroupId { get; set; }
/// <summary>
/// 任务信息(生成失败提示等)
/// </summary>
public string TaskMsg { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,37 @@
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Infrastructure.LambdaHelp;
using SqlSugar;
namespace JSMachine.WMS.App.Dto.Bo
{
/// <summary>
/// 全文搜索分页查询条件
/// </summary>
public class FullTextSearchByPageConditionBo
{
/// <summary>
/// 当前页
/// </summary>
public int CurrentPage { get; set; }
/// <summary>
/// 每页显示的条数
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 要排序的字段
/// </summary>
public string OrderByFiled { get; set; }
/// <summary>
///排序方式
/// </summary>
public OrderByType OrderByType { get; set; }
/// <summary>
/// 全文搜索类型
/// </summary>
public FullTextSearchType FullTextSearchType { get; set; }
/// <summary>
/// 关键字
/// </summary>
public string KeyWords { get; set; }
}
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
public sealed class DeviceStateDto
{
/// <summary>
/// PLC连接状态
/// </summary>
public bool PLCConected { get; set; } = false;
/// <summary>
/// PLC是否显示
/// </summary>
public bool PLCVisible { get; set; } = false;
/// <summary>
/// DB连接状态
/// </summary>
public bool DBConected { get; set; } = false;
/// <summary>
/// DB是否显示
/// </summary>
public bool DBVisible { get; set; } = false;
/// <summary>
/// 扫码器1连接状态
/// </summary>
public bool CS1Conected { get; set; } = false;
/// <summary>
/// 扫码器1显示状态
/// </summary>
public bool CS1Visible { get; set; } = false;
/// <summary>
/// 扫码器2连接状态
/// </summary>
public bool CS2Conected { get; set; } = false;
/// <summary>
/// 扫码器2显示状态
/// </summary>
public bool CS2Visible { get; set; } = false;
/// <summary>
/// ERP连接状态
/// </summary>
public bool ERPConected { get; set; } = false;
/// <summary>
/// ERP显示状态
/// </summary>
public bool ERPVisible { get; set; } = false;
/// <summary>
/// ACS连接状态
/// </summary>
public bool ACSConected { get; set; } = false;
/// <summary>
/// ACS显示状态
/// </summary>
public bool ACSVisible { get; set; } = false;
/// <summary>
/// 地磅连接状态
/// </summary>
public bool WBConected { get; set; } = false;
/// <summary>
/// 地磅显示状态
/// </summary>
public bool WBVisible { get; set; } = false;
/// <summary>
/// 打印机连接状态
/// </summary>
public bool PrintConected { get; set; } = false;
/// <summary>
/// 打印机显示状态
/// </summary>
public bool PrintVisible { get; set; } = false;
/// <summary>
/// 进纸端Wifi连接状态
/// </summary>
public bool WifiConected { get; set; } = false;
/// <summary>
/// 进纸端Wifi显示状态
/// </summary>
public bool WifiVisible { get; set; } = false;
}
}
@@ -0,0 +1,145 @@
using JSMachine.WMS.App.Dto.Enum;
using Prism.Mvvm;
namespace JSMachine.WMS.App.Dto
{
public partial class ERPTaskDto: IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 库位编号(起点)
/// </summary>
public string StorageRackNo { get; set; }
/// <summary>
/// 订单号
/// </summary>
public string OrderNo { get; set; }
/// <summary>
/// 生产退库单号
/// </summary>
public string ProduceReturnCode { get; set; }
/// <summary>
/// 销售单号
/// </summary>
public string SaleNo { get; set; }
/// <summary>
/// 销售退库单号
/// </summary>
public string SaleReturnCode { get; set; }
/// <summary>
/// 合格证号
/// </summary>
public string BarCode { get; set; }
/// <summary>
/// 用户名称
/// </summary>
public string CustomName { get; set; }
/// <summary>
/// 物料名称
/// </summary>
public string MateriaName { get; set; }
/// <summary>
/// 物料类型
/// </summary>
public string MateriaType { get; set; }
/// <summary>
/// 批次号的生成规则为: 作业单号+日期+班次
/// </summary>
public string MateriaBatch { get; set; }
/// <summary>
/// 物料编号
/// </summary>
public string MateriaCode { get; set; }
/// <summary>
/// 联动线出口,机械手设备唯一号
/// </summary>
public string EquipmentNo { get; set; }
/// <summary>
/// erp下发命令中出入库总数量
/// </summary>
public int ErpOrderNum { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public TaskStatusEnum TaskStatus { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public TaskTypeEnum TaskType { get; set; }
/// <summary>
/// 楼层(入库,尾托回库指定楼层使用)
/// </summary>
public FloorEnum Floor { get; set; }
/// <summary>
/// 码头位置 1:北区 2:南区 3:人工 4:退货到车间 99:其它
/// </summary>
public TransportEnum Transport { get; set; }
/// <summary>
/// 优先级 1:入库 10:出库 20:托盘入库 30:托盘出库 40:移库 99:其它
/// </summary>
public PriorityEnum Priority { get; set; }
/// <summary>
/// 锁定 false:未锁定 true:锁定
/// </summary>
public bool IsLock { get; set; }
/// <summary>
/// 已上传给Erp的数量
/// </summary>
public int AlreadyUploadNum { get; set; }
/// <summary>
/// Erp出库撤单数量
/// </summary>
public int ErpCancelNum { get; set; }
/// <summary>
/// 回调Erp失败/成功 1:成功 0:失败
/// </summary>
public bool IsCallBackErpEnd { get; set; }
/// <summary>
/// Erp回调Wms接口地址
/// </summary>
public string ErpCallWmsInterface { get; set; }
/// <summary>
/// Wms回调Erp接口地址
/// </summary>
public string WmsCallErpInterface { get; set; }
/// <summary>
/// 任务信息(生成失败提示等)
/// </summary>
public string TaskMsg {get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
/// <summary>
/// 提升机取货信号统计信息
/// </summary>
public class ElevatorCountDto : IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 提升机编号(1 2)
/// </summary>
public int ElevatorNo { get; set; }
/// <summary>
/// 通道号(网带编号)
/// </summary>
public int ElevatorChannel { get; set; }
/// <summary>
/// 数量(放货口是wms实际放货的数量,取货口是PLC给的取货信号的次数,如果有货的时候重启wms会造成计数多一个)
/// </summary>
public int Count { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,36 @@
using Prism.Mvvm;
namespace JSMachine.WMS.App.Dto
{
public partial class ElevatorPlcQueueDto : BindableBase, IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 提升机编号(1 2)
/// </summary>
public int ElevatorNo { get; set; }
/// <summary>
/// 通道号(网带编号)
/// </summary>
public int ElevatorChannel { get; set; }
/// <summary>
/// AgvTask的Id
/// </summary>
public Guid AgvTaskId { get; set; }
/// <summary>
/// 是否锁定
/// </summary>
public bool IsLock { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
///扫码信息来源
/// </summary>
public enum BarCodeSource
{
/// <summary>
/// 扫码器扫描
/// </summary>
CodeScanner = 0,
/// <summary>
/// 手持PDA上传
/// </summary>
PDA = 1
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 回调Erp失败/成功 1:成功 0:失败
/// </summary>
public enum CallBackErpEndEnum
{
/// <summary>
/// 失败
/// </summary>
fail = 0,
/// <summary>
/// 成功
/// </summary>
success = 1
}
}
@@ -0,0 +1,35 @@
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 库位状态
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum StorageRackStatus
{
/// <summary>
/// 空闲
/// </summary>
Empty = 0,
/// <summary>
/// 有货
/// </summary>
HaveGoods = 1,
/// <summary>
/// 锁定
/// </summary>
Locked = 2,
/// <summary>
/// 禁用
/// </summary>
Disabled = 3,
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// Erp数据上传状态
/// </summary>
public enum ErpUploadStatusEnum
{
/// <summary>
/// 等待上传
/// </summary>
WaittingToUpload = 0,
/// <summary>
/// 上传成功
/// </summary>
UploadSuccess = 1,
/// <summary>
/// 上传失败
/// </summary>
UploadFailed = 2,
/// <summary>
/// 接口请求成功但数据被ERP拒收(可能是数据不符合ERP要求,但接口已经请求成功,此种情况系统仅做记录不处理)
/// </summary>
Refused = 3,
/// <summary>
/// 无需上传
/// </summary>
Unnecessary = 4
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 楼层
/// </summary>
public enum FloorEnum
{
/// <summary>
/// 一楼
/// </summary>
FirstFloor=10,
/// <summary>
/// 二楼
/// </summary>
SecondFloor=20
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 全文搜索类型
/// </summary>
public enum FullTextSearchType
{
/// <summary>
/// 精准搜索
/// </summary>
Equals=0,
/// <summary>
/// 模糊匹配
/// </summary>
Like=1
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 锁定/未锁定
/// </summary>
public enum LockEnum
{
/// <summary>
/// 未锁定
/// </summary>
noLock = 0,
/// <summary>
/// 锁定
/// </summary>
locked = 1
}
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
public enum PrintType
{
/// <summary>
/// 新卷入库
/// </summary>
NewStock=0,
/// <summary>
/// 出库
/// </summary>
StockOut=1,
/// <summary>
/// 残卷回库
/// </summary>
ReStock=2
}
}
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 优先级(参照RCS来)
/// </summary>
public enum PriorityEnum
{
/// <summary>
/// 高
/// </summary>
High = 4,
/// <summary>
/// 中
/// </summary>
Middle = 6,
/// <summary>
/// 低
/// </summary>
Low = 8
}
}
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 残卷入库来源
/// </summary>
public enum ReStockInOriginEnum
{
/// <summary>
/// 自动入库
/// </summary>
AutoStockIn = 1,
/// <summary>
/// 编辑补录
/// </summary>
HandStockIn = 2,
/// <summary>
/// 新增补录
/// </summary>
AddHandStockIn = 3,
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 入库类型
/// </summary>
public enum StockInTypeEnum
{
/// <summary>
/// 新卷入库
/// </summary>
NewStockIn = 1,
/// <summary>
/// 新卷出库
/// </summary>
NewStockOut = 2,
/// <summary>
/// 残卷入库
/// </summary>
ReStockIn = 3,
/// <summary>
/// 残卷出库
/// </summary>
ReStockOut = 4
}
}
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 工单任务状态
/// </summary>
public enum TaskStatusEnum
{
/// <summary>
///待执行
/// </summary>
[Description("待执行")]
Initial = 0,
/// <summary>
/// 执行中
/// </summary>
[Description("执行中")]
InExecution = 1,
/// <summary>
/// 执行成功
/// </summary>
[Description("执行成功")]
Completed = 2,
/// <summary>
/// 执行失败
/// </summary>
[Description("执行失败")]
Failed = 3,
/// <summary>
/// 暂停
/// </summary>
[Description("暂停")]
Paused = 4,
/// <summary>
/// 取货完成
/// </summary>
[Description("取货完成")]
PickupCompleted = 5,
/// <summary>
/// 取消
/// </summary>
[Description("取消")]
Cancelled = -1
}
}
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 任务类型
/// </summary>
public enum TaskTypeEnum
{
/// <summary>
/// 码垛入库
/// </summary>
[Description("码垛入库")]
MachineHandIn = 1,
/// <summary>
/// PDA入库
/// </summary>
[Description("PDA入库")]
PdaIn = 2,
/// <summary>
/// 销售出库
/// </summary>
[Description("销售出库")]
SaleOut = 3,
/// <summary>
/// 生产退库(出库的一种,只不过生产退库目的地为退库区)
/// </summary>
[Description("生产退库")]
ProductionOut = 4,
/// <summary>
/// 尾拖回库(流程和入库一样,但是数据不上传erp)
/// </summary>
[Description("尾拖回库")]
TailIn = 5,
/// <summary>
/// 搬运(清除起点物料信息,更新终点物料信息)
/// </summary>
[Description("搬运")]
Carry = 6,
/// <summary>
/// 中转入库(入库时没有可用库位,或者列被锁定,需要先进入中转区)
/// </summary>
[Description("中转入库")]
TransferStockIn = 7,
/// <summary>
/// 中转出库(出库暂无提升机,临时进入中转区的货物,需要再次进入提升机出库,需要关联出库的erp任务)
/// </summary>
[Description("中转出库")]
TransferStockOut = 8,
/// <summary>
/// 出库掏货(出库时前面有不同批次货物阻挡,需要将阻挡获取先运送到中转区)
/// </summary>
[Description("出库掏货")]
RemoveStockOutObstruction = 9
}
}
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 码头位置
/// </summary>
public enum TransportEnum
{
/// <summary>
/// 北区(出库区) 定义和实际和南区是反的 实际为南区
/// </summary>
NorthArea = 1,
/// <summary>
/// 南区(出库区) 定义和实际和北区是反的 实际为北区
/// </summary>
SouthArea = 2,
/// <summary>
/// 人工入库区
/// </summary>
ManualArea = 3,
/// <summary>
/// 退库区
/// </summary>
ReturnStockArea = 4,
/// <summary>
/// 中间出库区
/// </summary>
MiddleOutArea=5,
/// <summary>
/// 中转区
/// </summary>
TransitArea=6,
/// <summary>
/// 提升机库区
/// </summary>
Elevator =98,
/// <summary>
/// 正常存储库区(入库区)
/// </summary>
NormalArea = 99
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto.Enum
{
/// <summary>
/// 企望接口记录上传状态
/// </summary>
public enum WantitErpUploadStatus
{
/// <summary>
/// 等待上传
/// </summary>
WaittingToUpload = 0,
/// <summary>
/// 上传成功
/// </summary>
UploadSuccess = 1,
/// <summary>
/// 上传失败
/// </summary>
UploadFailed = 2,
/// <summary>
/// 接口请求成功但数据被ERP拒收(可能是数据不符合ERP要求,但接口已经请求成功,此种情况系统仅做记录不处理)
/// </summary>
Refused = 3
}
}
@@ -0,0 +1,13 @@
using System;
namespace JSMachine.WMS.App.Dto
{
public interface IRootDto
{
public Guid Id { get; set; }
public DateTime? CreationTime { get; set; }
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,46 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity.Enums;
using Prism.Mvvm;
namespace JSMachine.WMS.Application.Dto
{
public partial class OperationLogDto : IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 操作来源
/// </summary>
public OperationSource OperationSource { get; set; }
/// <summary>
/// 操作类型
/// </summary>
public OperationType OperationType { get; set; }
/// <summary>
/// 用户ID
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 旧对象数据
/// </summary>
public string OldObject { get; set; }
/// <summary>
/// 新对象数据
/// </summary>
public string NewObject { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,66 @@
using JSMachine.WMS.Domain.IRepository;
using System.Collections.Generic;
namespace JSMachine.WMS.App.Dto
{
public class PagedResultDto<T>
{
public static readonly PagedResultDto<T> EmptyPagedResult = new PagedResultDto<T>(0, 0, 0, 0, null);
#region Public Properties
/// <summary>
/// 总记录数
/// </summary>
public int TotalRecords { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int TotalPages { get; set; }
/// <summary>
/// 每页的记录数
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 当前页码
/// </summary>
public int PageNumber { get; set; }
/// <summary>
/// 获取或设置当前页码的记录
/// </summary>
public List<T> PageData { get; set; }
#endregion
public PagedResultDto()
{
this.PageData = new List<T>();
}
public PagedResultDto(int totalRecords, int totalPages, int pageSize, int pageNumber, List<T> data)
{
this.TotalPages = totalPages;
this.TotalRecords = totalRecords;
this.PageSize = pageSize;
this.PageNumber = pageNumber;
this.PageData = data;
}
public PagedResultDto(int totalRecords, int pageSize, int pageNumber, List<T> data)
{
if (pageSize > 0)
{
this.TotalPages = totalRecords % pageSize == 0 ? totalRecords / pageSize : totalRecords / pageSize + 1;
}
this.TotalRecords = totalRecords;
this.PageSize = pageSize;
this.PageNumber = pageNumber;
this.PageData = data;
}
}
}
@@ -0,0 +1,145 @@
namespace JSMachine.WMS.App.Dto
{
/// <summary>
/// 原纸绑定机台信息
/// </summary>
public class PaperBindInfoDto
{
/// <summary>
/// 此条码入库重量
/// </summary>
public int ReceiveQty { get; set; }
/// <summary>
/// 材质
/// </summary>
public string Paper { get; set; }
/// <summary>
/// 供应商编号
/// </summary>
public string SuppID { get; set; }
/// <summary>
/// 条码
/// </summary>
public string Barcode { get; set; }
/// <summary>
/// 幅宽
/// </summary>
public string PaperWidth { get; set; }
/// <summary>
/// 米数(米)
/// </summary>
public string StockMeter { get; set; }
public string WorkingGroup { get; set; }
/// <summary>
/// 入库单号
/// </summary>
public string ReceiveNote { get; set; }
/// <summary>
/// 规格型号说明
/// </summary>
public string StockDescription { get; set; }
/// <summary>
/// 克重
/// </summary>
public string GPerM2 { get; set; }
/// <summary>
/// 库区
/// </summary>
public string Location { get; set; }
/// <summary>
/// 单价
/// </summary>
public float UnitPrice { get; set; }
/// <summary>
/// 可用状态
/// X表示不可使用
/// NULL、空、V均表示可用
/// </summary>
public string CanUse { get; set; }
/// <summary>
/// 物料号
/// </summary>
public string StockItem { get; set; }
/// <summary>
/// 残卷标志
/// </summary>
public string StockMark { get; set; }
/// <summary>
/// 物料号
/// </summary>
public string StockType { get; set; }
/// <summary>
/// 该纸卷库存的唯一身份标记
/// </summary>
public string ObjID { get; set; }
/// <summary>
///
/// </summary>
public string GroupManagementDistributedFactoryID { get; set; }
/// <summary>
/// 重量(公斤)
/// </summary>
public string ActualQty { get; set; }
/// <summary>
/// 供应商简称
/// </summary>
public string SuppShortName { get; set; }
/// <summary>
/// 制造商
/// </summary>
public string Maker { get; set; }
/// <summary>
/// 库位
/// </summary>
public string LocSub { get; set; }
/// <summary>
///
/// </summary>
public int ReceiveDiameter { get; set; }
/// <summary>
/// 手动指定机台号
/// </summary>
public int HandMachineNum { get; set; }
/// <summary>
/// 进纸状态(0:未进纸 1:已进纸)
/// </summary>
public int InPaperState { get; set; }
/// <summary>
/// 需要的纸质(未代纸情况下此值和实际纸质相等,代纸情况下则不等)
/// </summary>
public string WantedPaperCode { get; set; }
/// <summary>
/// 需要的幅宽(未代纸情况下此值和实际幅宽相等,代纸情况下则不等)
/// </summary>
public int WantedPaperWidth { get; set; }
/// <summary>
/// 代纸状态(0: 未代纸 1:已代纸)
/// </summary>
public int ReplacePaperState { get; set; }
/// <summary>
/// 需要的机台(未代纸情况下此值和实际机台相等,代纸情况下则不等)
/// </summary>
public int WantedMachineNum { get; set; }
/// <summary>
/// 纸质
/// </summary>
public string PaperMassCode { get; set; }
/// <summary>
/// 幅宽
/// </summary>
public int Web { get; set; }
/// <summary>
/// 机台
/// </summary>
public int MachineNum { get; set; }
}
}
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
/// <summary>
/// 原纸绑定机台信息
/// </summary>
public class PaperBindNumDto
{
/// <summary>
/// 此条码入库重量
/// </summary>
public int ReceiveQty { get; set; }
/// <summary>
/// 材质
/// </summary>
public string Paper { get; set; }
/// <summary>
/// 供应商编号
/// </summary>
public string SuppID { get; set; }
/// <summary>
/// 条码
/// </summary>
public string Barcode { get; set; }
/// <summary>
/// 幅宽
/// </summary>
public string PaperWidth { get; set; }
/// <summary>
/// 米数(米)
/// </summary>
public string StockMeter { get; set; }
public string WorkingGroup { get; set; }
/// <summary>
/// 入库单号
/// </summary>
public string ReceiveNote { get; set; }
/// <summary>
/// 规格型号说明
/// </summary>
public string StockDescription { get; set; }
/// <summary>
/// 克重
/// </summary>
public string GPerM2 { get; set; }
/// <summary>
/// 库区
/// </summary>
public string Location { get; set; }
/// <summary>
/// 单价
/// </summary>
public float UnitPrice { get; set; }
/// <summary>
/// 可用状态
/// X表示不可使用
/// NULL、空、V均表示可用
/// </summary>
public string CanUse { get; set; }
/// <summary>
/// 物料号
/// </summary>
public string StockItem { get; set; }
/// <summary>
/// 残卷标志
/// </summary>
public string StockMark { get; set; }
/// <summary>
/// 物料号
/// </summary>
public string StockType { get; set; }
/// <summary>
/// 该纸卷库存的唯一身份标记
/// </summary>
public string ObjID { get; set; }
/// <summary>
///
/// </summary>
public string GroupManagementDistributedFactoryID { get; set; }
/// <summary>
/// 重量(公斤)
/// </summary>
public string ActualQty { get; set; }
/// <summary>
/// 供应商简称
/// </summary>
public string SuppShortName { get; set; }
/// <summary>
/// 制造商
/// </summary>
public string Maker { get; set; }
/// <summary>
/// 库位
/// </summary>
public string LocSub { get; set; }
/// <summary>
///
/// </summary>
public int ReceiveDiameter { get; set; }
/// <summary>
/// 手动指定机台号
/// </summary>
public int HandMachineNum { get; set; }
/// <summary>
/// 进纸状态(0:未进纸 1:已进纸)
/// </summary>
public int InPaperState { get; set; }
/// <summary>
/// 需要的纸质(未代纸情况下此值和实际纸质相等,代纸情况下则不等)
/// </summary>
public string WantedPaperCode { get; set; }
/// <summary>
/// 需要的幅宽(未代纸情况下此值和实际幅宽相等,代纸情况下则不等)
/// </summary>
public int WantedPaperWidth { get; set; }
/// <summary>
/// 代纸状态(0: 未代纸 1:已代纸)
/// </summary>
public int ReplacePaperState { get; set; }
/// <summary>
/// 需要的机台(未代纸情况下此值和实际机台相等,代纸情况下则不等)
/// </summary>
public int WantedMachineNum { get; set; }
}
}
@@ -0,0 +1,174 @@
using Prism.Mvvm;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace JSMachine.WMS.App.Dto
{
public partial class PaperNewStockInDto : BindableBase, IRootDto
{
public Guid Id { get; set; }
private string _paperLabel;
/// <summary>
/// 原纸卷标
/// </summary>
public string PaperLabel
{
get => _paperLabel;
set => SetProperty(ref _paperLabel, value);
}
private string _productLineName;
/// <summary>
/// 产线名称
/// </summary>
public string ProductLineName
{
get => _productLineName;
set => SetProperty(ref _productLineName, value);
}
private string _supplier;
/// <summary>
/// 供应商
/// </summary>
public string Supplier
{
get => _supplier;
set => SetProperty(ref _supplier, value);
}
private string _makerName;
/// <summary>
/// 制造商
/// </summary>
public string MakerName
{
get => _makerName;
set => SetProperty(ref _makerName, value);
}
private string _stockHouseName;
/// <summary>
/// 仓库名称
/// </summary>
public string StockHouseName
{
get => _stockHouseName;
set => SetProperty(ref _stockHouseName, value);
}
private string _stockInType;
/// <summary>
/// 入库类型
/// </summary>
public string StockInType
{
get => _stockHouseName;
set => SetProperty(ref _stockHouseName, value);
}
private string _locationMark;
/// <summary>
/// 库位
/// </summary>
public string LocationMark
{
get => _locationMark;
set => SetProperty(ref _locationMark, value);
}
private int _paperUnitWeight;
/// <summary>
/// 原纸克重
/// </summary>
public int PaperUnitWeight
{
get => _paperUnitWeight;
set => SetProperty(ref _paperUnitWeight, value);
}
private string _paperCode;
/// <summary>
/// 原纸编码(纸质)
/// </summary>
public string PaperCode
{
get => _paperCode;
set => SetProperty(ref _paperCode, value);
}
private int _paperWidth;
/// <summary>
/// 门幅
/// </summary>
public int PaperWidth
{
get => _paperWidth;
set => SetProperty(ref _paperWidth, value);
}
private decimal _inWeight;
/// <summary>
/// 入库重量
/// </summary>
public decimal InWeight
{
get => _inWeight;
set => SetProperty(ref _inWeight, value);
}
private decimal _meterLength;
/// <summary>
/// 米长
/// </summary>
public decimal MeterLength
{
get => _meterLength;
set => SetProperty(ref _meterLength, value);
}
private string _memo;
/// <summary>
/// 备注
/// </summary>
public string Memo
{
get => _memo;
set => SetProperty(ref _memo, value);
}
private int _printState;
/// <summary>
/// 打印状态(0:未打印 1:已打印)
/// </summary>
public int PrintState
{
get => _printState;
set => SetProperty(ref _printState, value);
}
private DateTime? _printTime;
/// <summary>
/// 打印时间
/// </summary>
public DateTime? PrintTime
{
get => _printTime;
set => SetProperty(ref _printTime, value);
}
private DateTime? _creationTime;
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime
{
get => _creationTime;
set => SetProperty(ref _creationTime, value);
}
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,182 @@
using JSMachine.WMS.App.Dto.Enum;
using Prism.Mvvm;
namespace JSMachine.WMS.App.Dto
{
public partial class PaperReStockInDto : BindableBase, IRootDto
{
public Guid Id { get; set; }
private string _paperLabel;
/// <summary>
/// 原纸卷标
/// </summary>
public string PaperLabel
{
get => _paperLabel;
set => SetProperty(ref _paperLabel, value);
}
/// <summary>
/// 产线名称
/// </summary>
public string ProductLineName { get; set; }
private string _supplier;
/// <summary>
/// 供应商
/// </summary>
public string Supplier
{
get => _supplier;
set => SetProperty(ref _supplier, value);
}
private string _makerName;
/// <summary>
/// 制造商
/// </summary>
public string MakerName
{
get => _makerName;
set => SetProperty(ref _makerName, value);
}
/// <summary>
/// 仓库名称
/// </summary>
public string _stockHouseName;
public string StockHouseName
{
get => _stockHouseName;
set => SetProperty(ref _stockHouseName, value);
}
private string _locationMark;
/// <summary>
/// 库位
/// </summary>
public string LocationMark
{
get => _locationMark;
set => SetProperty(ref _locationMark, value);
}
/// <summary>
/// 入库类型
/// </summary>
public string StockInType { get; set; }
private int _paperUnitWeight;
/// <summary>
/// 原纸克重
/// </summary>
public int PaperUnitWeight
{
get => _paperUnitWeight;
set => SetProperty(ref _paperUnitWeight, value);
}
private string _paperCode;
/// <summary>
/// 原纸编码(纸质)
/// </summary>
public string PaperCode
{
get => _paperCode;
set => SetProperty(ref _paperCode, value);
}
private int _paperWidth;
/// <summary>
/// 门幅
/// </summary>
public int PaperWidth
{
get => _paperWidth;
set => SetProperty(ref _paperWidth, value);
}
private decimal _inWeight;
/// <summary>
/// 入库重量
/// </summary>
public decimal InWeight
{
get=> _inWeight;
set => SetProperty(ref _inWeight, value);
}
private decimal _meterLength;
/// <summary>
/// 米长
/// </summary>
public decimal MeterLength
{
get => _meterLength;
set => SetProperty(ref _meterLength, value);
}
private string _teamName;
/// <summary>
/// 班组
/// </summary>
public string TeamName
{
get => _teamName;
set => SetProperty(ref _teamName, value);
}
/// <summary>
/// 批次Id
/// </summary>
public Guid? BathId { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Memo { get; set; }
/// <summary>
/// 打印时间
/// </summary>
public DateTime? PrintTime { get; set; }
private DateTime? _creationTime;
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime
{
get => _creationTime;
set => SetProperty(ref _creationTime, value);
}
public DateTime? LastModifiedTime { get; set; }
/// <summary>
/// 来源
/// </summary>
public ReStockInOriginEnum Origin { get; set; }
/// <summary>
/// 企望接口入库记录上传状态
/// 0-等待上传,
///1-上传成功,
///2-上传失败
///3-接口请求成功但数据被ERP拒收(可能是数据不符合ERP要求,但接口已经请求成功,此种情况系统仅做记录不处理)
/// </summary>
public WantitErpUploadStatus WantitErpUploadStatus { get; set; }
/// <summary>
/// 信息来源(0-扫码器扫描 1-手持PDA上传)
/// </summary>
public BarCodeSource BarCodeSource { get; set; }
/// <summary>
/// Erp接口调用成功,但是返回入库失败
/// </summary>
public string ErpStockInMsg { get; set; }
}
}
@@ -0,0 +1,201 @@
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Domain.Entity.Enums;
using Prism.Mvvm;
namespace JSMachine.WMS.App.Dto
{
public partial class PaperStockOutDto : BindableBase, IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// ACS中的Id
/// </summary>
public Guid AcsId { get; set; }
/// <summary>
/// 结束状态
/// </summary>
public int FinishState { get; set; }
/// <summary>
/// 每批所包含的全部Id
/// </summary>
public string Ids { get; set; }
/// <summary>
/// 新卷残卷Id
/// </summary>
public Guid NewStockReStockID { get; set; }
private string _paperLable;
/// <summary>
/// 原纸卷标
/// </summary>
public string PaperLabel
{
get => _paperLable;
set => SetProperty(ref _paperLable, value);
}
private string _productLineName;
/// <summary>
/// 产线名称
/// </summary>
public string ProductLineName
{
get => _productLineName;
set => SetProperty(ref _productLineName, value);
}
private string _supplier;
/// <summary>
/// 供应商
/// </summary>
public string Supplier
{
get => _supplier;
set => SetProperty(ref _supplier, value);
}
private string _makerName;
/// <summary>
/// 制造商
/// </summary>
public string MakerName
{
get => _makerName;
set => SetProperty(ref _makerName, value);
}
private int _paperUnitWeight;
/// <summary>
/// 原纸克重
/// </summary>
public int PaperUnitWeight
{
get => _paperUnitWeight;
set => SetProperty(ref _paperUnitWeight, value);
}
private string _paperCode;
/// <summary>
/// 原纸编码(纸质)
/// </summary>
public string PaperCode
{
get => _paperCode;
set => SetProperty(ref _paperCode, value);
}
private int _paperWidth;
/// <summary>
/// 门幅
/// </summary>
public int PaperWidth
{
get => _paperWidth;
set => SetProperty(ref _paperWidth, value);
}
private decimal _outWeight;
/// <summary>
/// 出库重量
/// </summary>
public decimal OutWeight
{
get => _outWeight;
set => SetProperty(ref _outWeight, value);
}
private string _stockHouseName;
/// <summary>
/// 仓库名称
/// </summary>
public string StockHouseName
{
get => _stockHouseName;
set => SetProperty(ref _stockHouseName, value);
}
/// <summary>
/// 原纸出库类型
/// </summary>
public string StockOutType { get; set; }
private decimal _meterLength;
/// <summary>
/// 米长
/// </summary>
public decimal MeterLength
{
get => _meterLength;
set => SetProperty(ref _meterLength, value);
}
/// <summary>
/// 机台
/// </summary>
public string MachineNum { get; set; }
/// <summary>
/// 库位
/// </summary>
public string LocationMark { get; set; }
/// <summary>
/// 是否在库(0:不在库 1:在库)
/// </summary>
public int InOutState { get; set; }
private string _memo;
/// <summary>
/// 备注
/// </summary>
public string Memo
{
get => _memo;
set => SetProperty(ref _memo, value);
}
/// <summary>
/// 企望接口出库记录上传状态
/// </summary>
public WantitErpUploadStatus WantitErpUploadStatus { get; set; }
/// <summary>
/// 扫码信息来源
/// </summary>
public BarCodeSource BarCodeSource { get; set; }
/// <summary>
/// 需要的纸质(未代纸情况下此值和实际纸质相等,代纸情况下则不等)
/// </summary>
public string WantedPaperCode { get; set; }
/// <summary>
/// 需要的幅宽(未代纸情况下此值和实际幅宽相等,代纸情况下则不等)
/// </summary>
public int WantedPaperWidth { get; set; }
private DateTime? _creationTime;
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime
{
get => _creationTime;
set => SetProperty(ref _creationTime, value);
}
public DateTime? LastModifiedTime { get; set; }
/// <summary>
/// Erp接口调用成功,但是返回入库失败
/// </summary>
public string ErpStockInMsg { get; set; }
/// <summary>
/// 备纸错误状态(0:正常 1:错误)
/// </summary>
public PreparationErrorStateEnum PreparationErrorState { get; set; }
}
}
@@ -0,0 +1,106 @@
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Domain.Entity.Enums;
using Prism.Mvvm;
namespace JSMachine.WMS.App.Dto
{
public partial class PreparationBindMachineDto: BindableBase, IRootDto
{
/// <summary>
/// 主键Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 原纸卷标
/// </summary>
public string PaperLabel { get; set; }
/// <summary>
/// 材质
/// </summary>
public string PaperMassCode { get; set; }
/// <summary>
/// 门幅
/// </summary>
public int PaperWidth { get; set; }
/// <summary>
/// 米长
/// </summary>
public decimal MeterLength { get; set; }
/// <summary>
/// 重量
/// </summary>
public decimal Weight { get; set; }
/// <summary>
/// 库位
/// </summary>
public string LocationMark { get; set; }
/// <summary>
/// 库区
/// </summary>
public string LocSub { get; set; }
/// <summary>
/// 供应商简称
/// </summary>
public string SuppShortName { get; set; }
/// <summary>
/// 原纸克重
/// </summary>
public int PaperUnitWeight { get; set; }
/// <summary>
/// 制造商
/// </summary>
public string Maker { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
public DateTime? LastModifiedTime { get; set; }
/// <summary>
/// 手动指定机台号
/// </summary>
public int HandMachineNum { get; set; }
/// <summary>
/// 进纸状态(0:未进纸 1:已进纸)
/// </summary>
public int InPaperState { get; set; }
/// <summary>
/// 需要的纸质(未代纸情况下为空,代纸情况下有值)
/// </summary>
public string WantedPaperCode { get; set; }
/// <summary>
/// 需要的幅宽(未代纸情况下为空,代纸情况下有值)
/// </summary>
public int WantedPaperWidth { get; set; }
/// <summary>
/// 代纸状态(0: 未代纸 1:已代纸)
/// </summary>
public int ReplacePaperState { get; set; }
/// <summary>
/// 需要的机台(未代纸情况下为空,代纸情况下有值)
/// </summary>
public int WantedMachineNum { get; set; }
/// <summary>
/// 该纸卷库存的唯一身份标记
/// </summary>
public string ObjID { get; set; }
}
}
@@ -0,0 +1,56 @@
using JSMachine.WMS.Domain.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
/// <summary>
/// rcs系统的任务信息。模拟测试用
/// </summary>
public class RcsTaskDto : IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 任务id
/// </summary>
public string TaskId { get; set; }
/// <summary>
/// wms上传时间
/// </summary>
public DateTime UploadTime { get; set; }
/// <summary>
/// 起点
/// </summary>
public string StartPositionCode { get; set; }
/// <summary>
/// 终点
/// </summary>
public string EndPositionCode { get; set; }
/// <summary>
/// 任务状态
/// 3:已取消
/// 5:发送失败
/// 6:运行中
///7:执行失败
///8:已完成
///9:已下发
///10:等待确认
///20:取货中
///21:取货完成
///22:放货中
///23:放货完成
/// 必须
/// </summary>
public int Status { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,27 @@
using JSMachine.WMS.App.Dto.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
public partial class ReAutoStockIn
{
/// <summary>
/// 原纸卷标
/// </summary>
public string PaperLabel { get; set; }
/// <summary>
/// 入库重量
/// </summary>
public decimal InWeight { get; set; }
/// <summary>
/// 扫码信息来源
/// </summary>
public BarCodeSource BarCodeSource { get; set; }
}
}
@@ -0,0 +1,25 @@
using System;
namespace JSMachine.WMS.App.Dto
{
/// <summary>
/// 应用层数据传输对象的公共基类,提供实体标识和审计时间字段。
/// </summary>
public class RootDto
{
/// <summary>
/// DTO 对应的实体唯一标识。
/// </summary>
public virtual string Id { get; set; }
/// <summary>
/// 数据创建时间。
/// </summary>
public virtual DateTime CreationTime { get; set; }
/// <summary>
/// 数据最后修改时间。
/// </summary>
public virtual DateTime LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
public class SelectRowsDto
{
public List<PaperBindInfoDto> PaperBindInfoDtos { get; set; }
}
}
@@ -0,0 +1,281 @@
using JSMachine.WMS.App.Dto.Enum;
using NPOI.SS.Formula.Functions;
using System.Diagnostics.CodeAnalysis;
namespace JSMachine.WMS.App.Dto
{
public partial class StorageRackDto : IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 库位编号
/// </summary>
public string StorageRackNo { get; set; }
/// <summary>
/// 途径提升机指定点的坐标
/// </summary>
public string AgvPassPointNum { get; set; }
/// <summary>
/// 提升机通道号(PLC定义的网带编号),提升机库位专用
/// </summary>
public int? ElevatorChannel { get; set; }
/// <summary>
/// 是否锁定
/// </summary>
public bool IsLock { get; set; }
/// <summary>
/// 库位状态 0-空闲 1-有货 2-锁定 3-禁用
/// </summary>
public StorageRackStatus Status
{
get
{
if (IsLock)
return StorageRackStatus.Locked;
if (IsDisabled)
return StorageRackStatus.Disabled;
if (!string.IsNullOrEmpty(BarCode))
return StorageRackStatus.HaveGoods;
return StorageRackStatus.Empty;
}
set { }
}
/// <summary>
/// 库位名称
/// </summary>
public string StorageRackName { get; set; }
/// <summary>
/// 所在的库区排序
/// </summary>
public int AreaNumSort { get; set; }
/// <summary>
/// 库位排序(同一列的库位排序号越大越在里面(以车前进方向为准))
/// </summary>
public int NumSort { get; set; }
/// <summary>
/// 所属库区
/// </summary>
public long? ReservoirAreaId { get; set; }
/// <summary>
/// 所属库区列(小车进入库位的方向为列)
/// </summary>
public long ReservoirAreaColumnId { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 所属楼层编号(10-一楼 20-二楼)
/// </summary>
public FloorEnum Floor { get; set; }
/// <summary>
/// AGV名称
/// </summary>
public string MachineName { get; set; }
/// <summary>
/// 确认时间
/// </summary>
public DateTime? ConfirmDate { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; }
/// <summary>
/// 合格证号
/// </summary>
public string BarCode { get; set; }
/// <summary>
/// 订单号
/// </summary>
public string OrderNo { get; set; }
/// <summary>
/// 客户名称
/// </summary>
public string CustomName { get; set; }
/// <summary>
/// 物料名称
/// </summary>
public string MateriaName { get; set; }
/// <summary>
/// 物料类型
/// </summary>
public string MateriaType { get; set; }
/// <summary>
/// 单个托盘上的物料数量
/// </summary>
public int MateriaNum { get; set; }
/// <summary>
/// 批次号的生成规则为: 作业单号+日期+班次
/// </summary>
public string MateriaBatch { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MateriaCode { get; set; }
/// <summary>
/// 联动线出口,机械手设备唯一号
/// </summary>
public string EquipmentNo { get; set; }
/// <summary>
/// 码头位置 1:北区 2:南区 3:人工 4:退货到车间 99:正常存储库位
/// </summary>
public TransportEnum Transport { get; set; }
/// <summary>
/// Erp回调Wms接口地址
/// </summary>
public string ErpCallWmsInterface { get; set; }
/// <summary>
/// Wms回调Erp接口地址
/// </summary>
public string WmsCallErpInterface { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
/// <summary>
/// 清空物料
/// </summary>
public void ClearMaterial()
{
BarCode = null;
OrderNo = null;
CustomName = null;
MateriaName = null;
MateriaType = null;
MateriaNum = 0;
MateriaBatch = null;
MateriaCode = null;
}
/// <summary>
/// 是否空白可用库位
/// </summary>
/// <returns></returns>
public bool IsEmpty()
{
return !this.IsLock && !this.IsDisabled && string.IsNullOrEmpty(this.BarCode);
}
/// <summary>
/// 清空物料并解锁库位
/// </summary>
public void ClearMaterialAndUnLock()
{
ClearMaterial();
IsLock = false;
}
/// <summary>
/// 绑定物料信息
/// </summary>
public void BingdingMaterial(MateriaInfo materiaInfo)
{
BarCode = materiaInfo.BarCode;
OrderNo = materiaInfo.OrderNo;
MateriaName = materiaInfo.MateriaName;
MateriaType = materiaInfo.MateriaType;
MateriaNum = int.Parse(materiaInfo.MateriaNum);
MateriaBatch = materiaInfo.MateriaBatch;
MateriaCode = materiaInfo.MateriaCode;
}
/// <summary>
/// 绑定物料信息并锁定库位
/// </summary>
public void BingdingMaterialAndLock(MateriaInfo materiaInfo)
{
BingdingMaterial(materiaInfo);
IsLock = true;
}
public class StorageRackDtoIEqualityComparer : IEqualityComparer<StorageRackDto>
{
public bool Equals(StorageRackDto? x, StorageRackDto? y)
{
return x.StorageRackNo == y.StorageRackNo;
}
public int GetHashCode([DisallowNull] StorageRackDto obj)
{
return obj.StorageRackNo.GetHashCode();
}
}
public class MateriaInfo
{
/// <summary>
/// 合格证号
/// </summary>
public string BarCode { get; set; }
/// <summary>
/// 工单号
/// </summary>
public string OrderNo { get; set; }
/// <summary>
/// 物料名称
/// </summary>
public string MateriaName { get; set; }
/// <summary>
/// 物料类型
/// </summary>
public string MateriaType { get; set; }
/// <summary>
/// 物料数量
/// </summary>
public string MateriaNum { get; set; }
/// <summary>
/// 批次号
/// </summary>
public string MateriaBatch { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MateriaCode { get; set; }
}
}
}
@@ -0,0 +1,30 @@
using JSMachine.WMS.App.Dto;
namespace JSMachine.WMS.App.Dto
{
public class UserInfoDto : IRootDto
{
public Guid Id { get; set; }
/// <summary>
/// 账号
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreationTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? LastModifiedTime { get; set; }
}
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.Dto
{
/// <summary>
/// 企望Erp上传状态
/// </summary>
public partial class WantitErpUploadStatusDto
{
/// <summary>
/// 状态代码
/// </summary>
public int StatusCode { get; set; }
/// <summary>
/// 状态描述
/// </summary>
public string StatusDes { get; set; }
}
}
@@ -0,0 +1,19 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.App.IService
{
/// <summary>
/// AGV 任务应用服务,负责任务数据的转换、持久化以及状态更新。
/// </summary>
public interface IAGVTaskService: IBaseService<AGVTaskDto, AGVTask>
{
/// <summary>
/// 仅更新 AGV 任务的状态和修改时间,避免覆盖任务的其他字段。
/// </summary>
/// <param name="agvTask">包含任务标识和目标状态的任务 DTO。</param>
/// <returns>数据库更新成功返回 <see langword="true"/>,否则返回 <see langword="false"/>。</returns>
public Task<bool> UpdateAgvTaskStatus(AGVTaskDto agvTask);
}
}
@@ -0,0 +1,330 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using JSMachine.WMS.Domain.Repository;
using SqlSugar;
using System.Data;
using System.Linq.Expressions;
namespace JSMachine.WMS.App.IService
{
public interface IBaseService<TDTO, TAggregateRoot>
where TAggregateRoot : AggregateRoot, IAggregateRoot
where TDTO : IRootDto
{
#region Add
/// <summary>
/// 添加单个实体
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
Task<bool> Add(TDTO entity, SqlSugarScope sqlSugarScope=null);
/// <summary>
/// 添加单个实体
/// </summary>
/// <param name="dto"></param>
/// <param name="ignoreColumns">要忽略的字段</param>
/// <returns></returns>
Task<bool> Add(TDTO dto, params string[] ignoreColumns);
/// <summary>
/// 批量插入实体
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
Task<bool> Add(List<TDTO> entities);
/// <summary>
/// 字典方式添加单个实体
/// </summary>
/// <param name="tableName">表名称</param>
/// <param name="dic"></param>
/// <returns></returns>
Task<bool> Add(string tableName, Dictionary<string, object> dic);
/// <summary>
/// 添加之前判断指定字段是否存在,不存在则添加
/// </summary>
/// <param name="entity"></param>
/// <param name="expression"></param>
/// <returns></returns>
Task<bool> AddIfNotExist(TDTO entity, Expression<Func<TDTO, bool>> expression);
#endregion
#region Delete
/// <summary>
/// 根据Id删除记录
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<bool> DeleteById(Guid id, SqlSugarScope sqlSugarScope=null);
/// <summary>
/// 根据Id批量删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
Task<bool> DeleteBatch(Guid[] ids);
/// <summary>
/// 根据实体删除
/// </summary>
/// <param name="aggregateRoot"></param>
/// <returns></returns>
Task<bool> DeleteByEntity(TDTO aggregateRoot);
/// <summary>
/// 根据实体批量删除
/// </summary>
/// <param name="aggregateRoots"></param>
/// <returns>受影响行数不等于实体数量视为删除失败</returns>
Task<bool> DeleteByEntity(List<TDTO> aggregateRoot);
/// <summary>
/// 根据条件删除
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
Task<bool> DeleByExpression(Expression<Func<TAggregateRoot, bool>> expression);
#endregion
#region Edit
/// <summary>
/// 更改单个实体
/// </summary>
/// <param name="aggregateRoot"></param>
/// <returns></returns>
Task<bool> EditSingal(TDTO aggregateRoot);
/// <summary>
/// 更改单个实体但忽略指定的列
/// </summary>
/// <param name="aggregateRoot"></param>
/// <param name="ignoredColumns">忽略指定的条件</param>
/// <returns></returns>
Task<bool> EditSingal(TDTO aggregateRoot, Expression<Func<TDTO, object>> ignoredColumns);
/// <summary>
/// 更改单个实体但忽略指定的列
/// </summary>
/// <param name="aggregateRoot"></param>
/// <param name="ignoredColumns">要忽略的列名</param>
/// <returns></returns>
Task<bool> EditSingal(TDTO aggregateRoot, string[] ignoredColumns);
/// <summary>
/// 更改单个实体中指定的列
/// </summary>
/// <param name="aggregateRoot"></param>
/// <param name="columns">要更改的列名</param>
/// <returns></returns>
Task<bool> EditSingalWithSpecificCols(TDTO aggregateRoot, string[] columns);
/// <summary>
/// 批量更新
/// </summary>
/// <param name="aggregateRoots"></param>
/// <returns></returns>
Task<bool> EditBatch(List<TDTO> aggregateRoots);
/// <summary>
/// 快速批量更新
/// </summary>
/// <param name="aggregateRoots"></param>
/// <returns></returns>
Task<bool> EditBatchFast(List<TDTO> aggregateRoots);
/// <summary>
/// 字典方式更改
/// </summary>
/// <param name="tableName"></param>
/// <param name="dic"></param>
/// <returns></returns>
Task<bool> EditByDic(string tableName, Dictionary<string, object> dic);
/// <summary>
/// 字典方式批量更改
/// </summary>
/// <param name="tableName"></param>
/// <param name="dic"></param>
/// <returns></returns>
Task<bool> EditByDic(string tableName, List<Dictionary<string, object>> dic);
#endregion
#region Query
/// <summary>
/// 查询所有
/// </summary>
/// <returns></returns>
Task<List<TDTO>> GetAll();
/// <summary>
/// 查询所有
/// </summary>
/// <returns></returns>
Task<List<TDTO>> GetAll(string orderByFiled, OrderByType orderByType);
/// <summary>
/// 分片查询所有
/// </summary>
/// <param name="fragmentSize">分片大小,分片越大效率越高,但是占用的内存也越大,需要合理考虑数据量和硬件性能确定此参数大小</param>
/// <returns></returns>
Task<List<TDTO>> GetAllFragment(int fragmentSize = 50000);
/// <summary>
/// 分片查询所有(带排序功能)
/// </summary>
/// <param name="orderByFiled">要排序的字段</param>
/// <param name="orderByType">排序方式</param>
/// <param name="fragmentSize">分片大小,分片越大效率越高,但是占用的内存也越大,需要合理考虑数据量和硬件性能确定此参数大小</param>
/// <returns></returns>
Task<List<TDTO>> GetAllFragment(string orderByFiled, OrderByType orderByType, int fragmentSize = 200);
/// <summary>
/// 根据条件查询数量
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
Task<int> Count(Expression<Func<TDTO, bool>> expression);
/// <summary>
/// 根据主键查询
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
Task<TDTO> QueryByKey(string key);
/// <summary>
/// 查询满足条件的第一个
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
Task<TDTO> GetSingalByExpression(Expression<Func<TDTO, bool>> expression);
/// <summary>
/// 查询满足条件的第一个(带排序)
/// </summary>
/// <param name="expression"></param>
/// <param name="orderByExpress"></param>
/// <param name="orderByType"></param>
/// <returns></returns>
Task<TDTO> GetSingalByExpression(
Expression<Func<TDTO, bool>> expression,
Expression<Func<TAggregateRoot, object>> orderByExpress,
OrderByType orderByType);
/// <summary>
/// 查询满足条件的所有实体
/// 注意 在oracle数据中时间段范围查询请使用 SqlFunc.Oracle_ToDate,可大幅提高查询速度
/// </summary>
/// <param name="aggregateRoot"></param>
/// <param name="expression"></param>
/// <returns></returns>
Task<List<TDTO>> GetListByExpression(Expression<Func<TDTO, bool>> expression);
/// <summary>
/// 查询满足条件的所有实体(带排序功能)
/// </summary>
/// <param name="expression"></param>
/// <param name="filedName"></param>
/// <param name="orderByType"></param>
/// <returns></returns>
Task<List<TDTO>> GetListByExpression(Expression<Func<TAggregateRoot, bool>> expression,
string filedName,
OrderByType orderByType = OrderByType.Desc);
/// <summary>
/// 通过实体查询满足条件的数据
/// </summary>
/// <param name="aggregateRoot"></param>
/// <returns></returns>
Task<List<TDTO>> GetListWhereClass(TDTO dto);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="pageIndex">当前页码</param>
/// <param name="pageSize"></param>
/// <param name="totalCount">总记录条数</param>
/// <param name="expression">查询条件</param>
/// <returns></returns>
Task<PagedResult<TDTO>> QueryByPage(PageQueryCondition commonQueryCondition);
/// <summary>
/// 根据主键批量查询
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
Task<List<TDTO>> QueryByIds(Guid[] ids);
/// <summary>
/// 是否存在
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
Task<bool> Exist(Expression<Func<TDTO, bool>> expression);
#endregion
#region RawSql Sql操作
/// <summary>
/// sql查询,不带参数
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
Task<List<TDTO>> GetBySql(string sql);
/// <summary>
/// sql查询,带参数 参数示例 select * from table where id=@id and name=@name 则字典的key为 "@id"
/// </summary>
/// <param name="sql"></param>
/// <param name="sqlParam"></param>
/// <returns></returns>
Task<List<TDTO>> GetBySql(string sql, Dictionary<string, object> sqlParam);
/// <summary>
/// sql查询匿名类型
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
Task<List<dynamic>> GetDynamicsBysql(string sql);
/// <summary>
/// sql查询匿名类型,附带参数 参数示例 select * from table where id=@id and name=@name 则字典的key为 "@id"
/// </summary>
/// <param name="sql"></param>
/// <param name="sqlParam"></param>
/// <returns></returns>
Task<List<dynamic>> GetDynamicsBysql(string sql, Dictionary<string, object> sqlParam);
/// <summary>
/// 通过sql查询DataTable
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
Task<DataTable> GetDataTableBysql(string sql);
/// <summary>
/// 通过sql查询DataTable, 参数示例 select * from table where id=@id and name=@name 则字典的key为 "@id"
/// </summary>
/// <param name="sql"></param>
/// <param name="sqlParam"></param>
/// <returns></returns>
Task<DataTable> GetDataTableBysql(string sql, Dictionary<string, object> sqlParam);
/// <summary>
/// 执行原生sql
/// </summary>
/// <param name="sql"></param>
/// <param name="params"></param>
/// <returns></returns>
Task<bool> ExcuteSql(string sql, params SugarParameter[] @params);
#endregion
#region 事务
/// <summary>
/// 开始事务
/// </summary>
/// <returns></returns>
Task BeginTran(SqlSugarScope sqlSugarScope);
/// <summary>
/// 提交事务
/// </summary>
/// <returns></returns>
Task CommitTran(SqlSugarScope sqlSugarScope);
/// <summary>
/// 回滚事务
/// </summary>
/// <returns></returns>
Task RollbackTran(SqlSugarScope sqlSugarScope);
SqlSugarTransaction UseTranAsync();
#endregion
}
}
@@ -0,0 +1,23 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.App.IService
{
public interface IERPTaskService: IBaseService<ERPTaskDto, ERPTask>
{
/// <summary>
/// 更新任务消息(生成失败提示等)
/// </summary>
/// <param name="id"></param>
/// <param name="taskMsg"></param>
/// <returns></returns>
Task<bool> UpdateTaskMsg(Guid id, string taskMsg);
/// <summary>
/// 更新任务状态(已完成、取消等)
/// </summary>
/// <param name="erpTask"></param>
/// <returns></returns>
Task<bool> UpdateTaskStatus(ERPTaskDto erpTask);
}
}
@@ -0,0 +1,22 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Domain.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.IService
{
public interface IElevatorCountService : IBaseService<ElevatorCountDto, ElevatorCount>
{
/// <summary>
/// 计数自增
/// </summary>
/// <param name="elevatorNo"></param>
/// <param name="channel"></param>
/// <returns></returns>
Task IncrementCount(int elevatorNo, int channel);
}
}
@@ -0,0 +1,11 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.App.IService
{
public interface IElevatorPlcQueueService : IBaseService<ElevatorPlcQueueDto, ElevatorPlcQueue>
{
}
}
@@ -0,0 +1,11 @@
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Application.Dto;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.Application.IService
{
public interface IOperationLogService : IBaseService<OperationLogDto, OperationLog>
{
// 可根据业务需求添加自定义方法
}
}
@@ -0,0 +1,9 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.App.IService
{
public interface IRcsTaskService : IBaseService<RcsTaskDto, RcsTask>
{
}
}
@@ -0,0 +1,67 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.App.IService
{
public interface IStorageRackService : IBaseService<StorageRackDto, StorageRack>
{
/// <summary>
/// 根据库位号查询库位信息
/// </summary>
/// <param name="storageRackNo"></param>
/// <returns></returns>
Task<StorageRackDto> GetByStorageRackNo(string storageRackNo);
/// <summary>
/// 根据提升机编号和通道号获取提所属库位信息
/// </summary>
/// <param name="elevatorNo"></param>
/// <param name="channel"></param>
/// <returns></returns>
Task<StorageRackDto> GetElevatorWareHouse(int elevatorNo,int channel);
/// <summary>
/// 获取可用的提升机库位
/// </summary>
/// <param name="floor"></param>
/// <returns></returns>
Task<List<StorageRackDto>> GetUsableElevatorWareHouse(FloorEnum floor);
/// <summary>
/// 锁定库位
/// </summary>
/// <param name="storageRack"></param>
/// <returns></returns>
Task<bool> Lock(StorageRackDto storageRack);
/// <summary>
/// 解锁库位
/// </summary>
/// <param name="storageRack"></param>
/// <returns></returns>
Task<bool> Unlock(StorageRackDto storageRack);
/// <summary>
/// 更新锁定状态
/// </summary>
/// <param name="id"></param>
/// <param name="isLock"></param>
/// <returns></returns>
Task<bool> UpdateLockStatus(Guid id, bool isLock);
/// <summary>
/// 更新禁用状态
/// </summary>
/// <param name="id"></param>
/// <param name="isDisable"></param>
/// <returns></returns>
Task<bool> UpdateDisableStatus(Guid id, bool isDisable);
/// <summary>
/// 预计算可用库位数量(用于pda入库请求时预计算可用库位数量,防止入库时找不到可用库位)
/// </summary>
/// <param name="floor"></param>
/// <returns></returns>
//Task<int> PreCalUsableStorageRackCount(FloorEnum floor);
/// <summary>
/// 获取可用的中转库位
/// </summary>
/// <returns></returns>
Task<StorageRackDto> GetUsableTransferStorageRack();
}
}
@@ -0,0 +1,10 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity;
namespace JSMachine.WMS.App.IService
{
public interface IUserInfoService : IBaseService<UserInfoDto, UserInfo>
{
// 可以在这里添加UserInfo特有的方法
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Prism.Core" Version="8.1.97" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JSMachine.WMS.Domain\JSMachine.WMS.Domain.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,21 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
namespace JSMachine.WMS.App.ServiceImpl
{
public class AGVTaskService : BaseService<AGVTaskDto, AGVTask>, IAGVTaskService
{
private IAGVTaskRepository _agvTaskRepository;
public AGVTaskService(IAGVTaskRepository repository) : base(repository)
{
_agvTaskRepository = repository;
}
public async Task<bool> UpdateAgvTaskStatus(AGVTaskDto agvTask)
{
return await base.EditSingalWithSpecificCols(agvTask, [nameof(AGVTask.TaskStatus)]);
}
}
}
@@ -0,0 +1,360 @@
using AutoMapper;
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using JSMachine.WMS.Domain.Repository;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.LambdaHelp;
using SqlSugar;
using System.Data;
using System.Linq.Expressions;
namespace JSMachine.WMS.App.ServiceImpl
{
/// <summary>
/// 应用层通用服务基类,负责 DTO 与领域实体之间的映射,
/// 并将增删改查请求委托给对应的领域仓储。
/// </summary>
/// <typeparam name="TDTO">应用层数据传输对象类型。</typeparam>
/// <typeparam name="TAggregateRoot">领域聚合根类型。</typeparam>
public class BaseService<TDTO, TAggregateRoot> : IBaseService<TDTO, TAggregateRoot>
where TAggregateRoot : AggregateRoot, IAggregateRoot
where TDTO : IRootDto
{
private IBaseRepository<TAggregateRoot> IBaseRepository;
private static IMapper _mapper = GlobalServericeProvidor.GetService<IMapper>();
public BaseService(IBaseRepository<TAggregateRoot> baseRepository)
{
IBaseRepository = baseRepository;
}
/// <summary>
/// 添加单个 DTO;当 DTO 未提供标识时生成新标识,并维护创建时间和修改时间。
/// </summary>
/// <param name="dto">待保存的数据传输对象。</param>
/// <param name="sqlSugarScope">可选的数据库作用域;为空时使用仓储默认作用域。</param>
/// <returns>数据库插入是否成功。</returns>
public async virtual Task<bool> Add(TDTO dto, SqlSugarScope sqlSugarScope=null)
{
if (dto.Id == Guid.Empty)
dto.Id = Guid.NewGuid();
dto.CreationTime = DateTime.Now;
dto.LastModifiedTime = DateTime.Now;
return await IBaseRepository.Add(_mapper.Map<TAggregateRoot>(dto),sqlSugarScope);
}
public async virtual Task<bool> Add(TDTO dto, params string[] ignoreColumns)
{
if (dto.Id == Guid.Empty)
dto.Id = Guid.NewGuid();
dto.CreationTime = DateTime.Now;
dto.LastModifiedTime = DateTime.Now;
return await IBaseRepository.Add(_mapper.Map<TAggregateRoot>(dto), ignoreColumns);
}
public async virtual Task<bool> Add(List<TDTO> entities)
{
if (entities == null || entities.Count == 0)
return false;
entities.ForEach(p =>
{
if (p.Id == Guid.Empty)
p.Id = Guid.NewGuid();
p.CreationTime = DateTime.Now;
p.LastModifiedTime = DateTime.Now;
});
return await IBaseRepository.Add(_mapper.Map<List<TAggregateRoot>>(entities));
}
public async Task<bool> Add(string tableName, Dictionary<string, object> dic)
{
if (dic.ContainsKey("Id"))
dic.Add("Id", Guid.NewGuid().ToString("N"));
return await IBaseRepository.Add(tableName, dic);
}
/// <summary>
/// 添加之前判断指定字段是否存在,不存在则添加
/// </summary>
/// <param name="entity"></param>
/// <param name="expression"></param>
/// <returns></returns>
public async Task<bool> AddIfNotExist(TDTO entity, Expression<Func<TDTO, bool>> expression)
{
return await IBaseRepository.AddIfNotExist(_mapper.Map<TAggregateRoot>(entity),
_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression));
}
public async Task<int> Count()
{
return await IBaseRepository.Count();
}
public async Task<int> Count(Expression<Func<TDTO, bool>> expression)
{
return await IBaseRepository.Count(_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression));
}
public async Task<bool> DeleByExpression(Expression<Func<TAggregateRoot, bool>> expression)
{
return await IBaseRepository.DeleByExpression(expression);
}
public async Task<bool> DeleteBatch(Guid[] ids)
{
return await IBaseRepository.DeleteBatch(ids);
}
public async Task<bool> DeleteByEntity(TDTO aggregateRoot)
{
return await IBaseRepository.DeleteByEntity(_mapper.Map<TAggregateRoot>(aggregateRoot));
}
public async Task<bool> DeleteByEntity(List<TDTO> aggregateRoot)
{
return await IBaseRepository.DeleteByEntity(_mapper.Map<List<TAggregateRoot>>(aggregateRoot));
}
public async Task<bool> DeleteById(Guid id, SqlSugarScope sqlSugarScope=null)
{
return await IBaseRepository.DeleteById(id,sqlSugarScope);
}
public async Task<bool> EditBatch(List<TDTO> dtos)
{
dtos.ForEach(p => p.LastModifiedTime = DateTime.Now);
return await IBaseRepository.EditBatch(_mapper.Map<List<TAggregateRoot>>(dtos));
}
/// <summary>
/// 快速批量更新
/// </summary>
/// <param name="dtos"></param>
/// <returns></returns>
public async Task<bool> EditBatchFast(List<TDTO> dtos)
{
dtos.ForEach(p => p.LastModifiedTime = DateTime.Now);
return await IBaseRepository.EditBatchFast(_mapper.Map<List<TAggregateRoot>>(dtos));
}
public async Task<bool> EditByDic(string tableName, Dictionary<string, object> dic)
{
return await IBaseRepository.EditByDic(tableName, dic);
}
public async Task<bool> EditByDic(string tableName, List<Dictionary<string, object>> dic)
{
return await IBaseRepository.EditByDic(tableName, dic);
}
public async virtual Task<bool> EditSingal(TDTO aggregateRoot)
{
aggregateRoot.LastModifiedTime = DateTime.Now;
//更新的时候不能够更改数据的添加时间
return await IBaseRepository.EditSingal(_mapper.Map<TAggregateRoot>(aggregateRoot), new string[] { "CreationTime" });
}
public async Task<bool> EditSingal(TDTO dto, Expression<Func<TDTO, object>> ignoredColumns)
{
dto.LastModifiedTime = DateTime.Now;
return await IBaseRepository.EditSingal(_mapper.Map<TAggregateRoot>(dto), _mapper.Map<Expression<Func<TAggregateRoot, object>>>(ignoredColumns));
}
public async Task<bool> EditSingal(TDTO dto, string[] ignoredColumns)
{
dto.LastModifiedTime = DateTime.Now;
return await IBaseRepository.EditSingal(_mapper.Map<TAggregateRoot>(dto), ignoredColumns);
}
public async Task<bool> EditSingalWithSpecificCols(TDTO dto, string[] columns)
{
dto.LastModifiedTime = DateTime.Now;
return await IBaseRepository.EditSingalWithSpecificCols(_mapper.Map<TAggregateRoot>(dto), columns);
}
public async Task<bool> Exist(Expression<Func<TDTO, bool>> expression)
{
return await IBaseRepository.Exist(_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression));
}
public async Task<List<TDTO>> GetAll()
{
List<TAggregateRoot> data = await IBaseRepository.GetAll();
return _mapper.Map<List<TDTO>>(data);
}
public async Task<List<TDTO>> GetAll(string orderByFiled, OrderByType orderByType)
{
List<TAggregateRoot> data = await IBaseRepository.GetAll(LambdaHelper<TAggregateRoot>.GetOrderExpression<object>(orderByFiled),
orderByType);
return _mapper.Map<List<TDTO>>(data);
}
public async Task<List<TDTO>> GetAllFragment(int fragmentSize = 50000)
{
List<TAggregateRoot> data = await IBaseRepository.GetAllFragment(fragmentSize);
return _mapper.Map<List<TDTO>>(data);
}
public async Task<List<TDTO>> GetAllFragment(string orderByFiled, OrderByType orderByType, int fragmentSize = 200)
{
List<TAggregateRoot> data = await IBaseRepository.GetAllFragment(
LambdaHelper<TAggregateRoot>.GetOrderExpression<object>(orderByFiled),
orderByType,
fragmentSize);
return _mapper.Map<List<TDTO>>(data);
}
public async Task<List<TDTO>> GetBySql(string sql)
{
List<TAggregateRoot> data = await IBaseRepository.GetBySql(sql);
return _mapper.Map<List<TDTO>>(data);
}
public async Task<List<TDTO>> GetBySql(string sql, Dictionary<string, object> sqlParam)
{
List<TAggregateRoot> data = await IBaseRepository.GetBySql(sql, sqlParam);
return _mapper.Map<List<TDTO>>(data);
}
public async Task<DataTable> GetDataTableBysql(string sql)
{
return await IBaseRepository.GetDataTableBysql(sql);
}
public async Task<DataTable> GetDataTableBysql(string sql, Dictionary<string, object> sqlParam)
{
return await IBaseRepository.GetDataTableBysql(sql, sqlParam);
}
public async Task<List<dynamic>> GetDynamicsBysql(string sql)
{
return await IBaseRepository.GetDynamicsBysql(sql);
}
public async Task<List<dynamic>> GetDynamicsBysql(string sql, Dictionary<string, object> sqlParam)
{
return await IBaseRepository.GetDynamicsBysql(sql, sqlParam);
}
public async Task<List<TDTO>> GetListByExpression(Expression<Func<TDTO, bool>> expression)
{
List<TAggregateRoot> data = await IBaseRepository.GetListByExpression(
_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression));
return _mapper.Map<List<TDTO>>(data);
}
/// <summary>
/// 查询满足条件的所有实体(带排序功能)
/// </summary>
/// <param name="expression"></param>
/// <param name="orderByExpress"></param>
/// <param name="orderByType">Asc = 0,Desc = 1</param>
/// <returns></returns>
public async Task<List<TDTO>> GetListByExpression(Expression<Func<TAggregateRoot, bool>> expression,
string filedName,
OrderByType orderByType = OrderByType.Desc)
{
List<TAggregateRoot> data = await IBaseRepository.GetListByExpression(
_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression),
filedName,
orderByType);
return _mapper.Map<List<TDTO>>(data);
}
/// <summary>
/// 通过实体查询满足条件的数据
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<List<TDTO>> GetListWhereClass(TDTO dto)
{
List<TAggregateRoot> data = await IBaseRepository.GetListWhereClass(
_mapper.Map<TAggregateRoot>(dto));
return _mapper.Map<List<TDTO>>(data);
}
public async Task<TDTO> GetSingalByExpression(Expression<Func<TDTO, bool>> expression)
{
TAggregateRoot data = await IBaseRepository.GetSingalByExpression(
_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression));
return _mapper.Map<TDTO>(data);
}
public async Task<TDTO> GetSingalByExpression(
Expression<Func<TDTO, bool>> expression,
Expression<Func<TAggregateRoot, object>> orderByExpress,
OrderByType orderByType)
{
TAggregateRoot data = await IBaseRepository.GetSingalByExpression(
_mapper.Map<Expression<Func<TAggregateRoot, bool>>>(expression), orderByExpress, orderByType);
return _mapper.Map<TDTO>(data);
}
public async Task<List<TDTO>> QueryByIds(Guid[] ids)
{
List<TAggregateRoot> data = await IBaseRepository.QueryByIds(ids);
return _mapper.Map<List<TDTO>>(data);
}
public async Task<TDTO> QueryByKey(string key)
{
TAggregateRoot data = await IBaseRepository.QueryByKey(key);
return _mapper.Map<TDTO>(data);
}
public async Task<PagedResult<TDTO>> QueryByPage(PageQueryCondition pageQueryCondition)
{
PagedResult<TAggregateRoot> dataPage = await IBaseRepository.QueryByPage(pageQueryCondition);
return new PagedResult<TDTO>(dataPage.TotalRecords,
dataPage.PageSize,
dataPage.PageNumber,
_mapper.Map<List<TDTO>>(dataPage.PageData));
}
public async Task<bool> ExcuteSql(string sql, params SugarParameter[] @params)
{
return await IBaseRepository.ExcuteSql(sql, @params);
}
#region 事务操作
public async Task BeginTran(SqlSugarScope sqlSugarScope)
{
await IBaseRepository.BeginTran(sqlSugarScope);
}
public async Task CommitTran(SqlSugarScope sqlSugarScope)
{
await IBaseRepository.CommitTran(sqlSugarScope);
}
public async Task RollbackTran(SqlSugarScope sqlSugarScope)
{
await IBaseRepository.RollbackTran(sqlSugarScope);
}
public SqlSugarTransaction UseTranAsync()
{
return IBaseRepository.UseTranAsync();
}
#endregion
}
}
@@ -0,0 +1,32 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.ServiceImpl
{
public class ERPTaskService : BaseService<ERPTaskDto, ERPTask>, IERPTaskService
{
private IERPTaskRepository _erpTaskRepository;
public ERPTaskService(IERPTaskRepository repository) : base(repository)
{
_erpTaskRepository = repository;
}
public Task<bool> UpdateTaskMsg(Guid id, string taskMsg)
{
return _erpTaskRepository.ExcuteSql($"update ERPTask set TaskMsg='{taskMsg}' where Id='{id}'");
}
public Task<bool> UpdateTaskStatus(ERPTaskDto erpTask)
{
erpTask.LastModifiedTime = DateTime.Now;
return base.EditSingalWithSpecificCols(erpTask, [nameof(ERPTaskDto.TaskStatus), nameof(ERPTaskDto.LastModifiedTime)]);
}
}
}
@@ -0,0 +1,49 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.ServiceImpl
{
public class ElevatorCountService : BaseService<ElevatorCountDto, ElevatorCount>, IElevatorCountService
{
public ElevatorCountService(IBaseRepository<ElevatorCount> baseRepository) : base(baseRepository)
{
}
/// <summary>
/// 计数自增
/// </summary>
/// <param name="elevatorNo"></param>
/// <param name="channel"></param>
/// <returns></returns>
public async Task IncrementCount(int elevatorNo, int channel)
{
ElevatorCountDto elevatorCount = await GetSingalByExpression(p =>
p.CreationTime >= DateTime.Now.Date &&
p.ElevatorNo == elevatorNo &&
p.ElevatorChannel == channel);
if (elevatorCount == null)
{
elevatorCount = new()
{
ElevatorNo = elevatorNo,
ElevatorChannel = channel,
Count = 1
};
await Add(elevatorCount);
}
else
{
elevatorCount.Count++;
await EditSingal(elevatorCount);
}
}
}
}
@@ -0,0 +1,18 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using JSMachine.WMS.Infrastructure.Helper;
namespace JSMachine.WMS.App.ServiceImpl
{
public class ElevatorPlcQueueService : BaseService<ElevatorPlcQueueDto, ElevatorPlcQueue>, IElevatorPlcQueueService
{
private IElevatorPlcQueueRepository _elevatorPlcQueueRepository;
public ElevatorPlcQueueService(IElevatorPlcQueueRepository repository) : base(repository)
{
_elevatorPlcQueueRepository = repository;
}
}
}
@@ -0,0 +1,20 @@
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Application.Dto;
using JSMachine.WMS.Application.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
namespace JSMachine.WMS.Application.ServiceImpl
{
public class OperationLogService : BaseService<OperationLogDto, OperationLog>, IOperationLogService
{
private readonly IOperationLogRepository _operationLogRepository;
public OperationLogService(IOperationLogRepository repository) : base(repository)
{
_operationLogRepository = repository;
}
// 可根据业务需求添加自定义操作方法
}
}
@@ -0,0 +1,21 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.App.ServiceImpl
{
public class RcsTaskService : BaseService<RcsTaskDto, RcsTask>, IRcsTaskService
{
private IRcsTaskRepository _rcsTaskRepository;
public RcsTaskService(IRcsTaskRepository rcsTaskRepository) : base(rcsTaskRepository)
{
_rcsTaskRepository = rcsTaskRepository;
}
}
}
@@ -0,0 +1,215 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using SqlSugar;
using static JSMachine.WMS.App.Dto.StorageRackDto;
namespace JSMachine.WMS.App.ServiceImpl
{
public class StorageRackService : BaseService<StorageRackDto, StorageRack>, IStorageRackService
{
private IStorageRackRepository _storageRackRepository;
public StorageRackService(IStorageRackRepository repository) : base(repository)
{
_storageRackRepository = repository;
}
/// <summary>
/// 根据库位号查询库位信息
/// </summary>
/// <param name="storageRackNo"></param>
/// <returns></returns>
public async Task<StorageRackDto> GetByStorageRackNo(string storageRackNo)
{
return await GetSingalByExpression(p => p.StorageRackNo == storageRackNo);
}
/// <summary>
/// 根据批次号计算目标存储区库位
/// </summary>
/// <param name="materiaBatch"></param>
/// <returns></returns>
//public async Task<StorageRackDto> CalculateDestWarehouse(string materiaBatch, FloorEnum floor)
//{
// //如果某一列有一个库位被锁定,说明该列有未完成的任务(任务完成会解锁库位),因此该列不可以再生成新的任务
// //获取楼层的所有库位
// List<StorageRackDto> wareHouses = await GetListByExpression(p =>
// p.Floor == floor && p.Transport == TransportEnum.NormalArea);
// if (wareHouses.IsNullOrEmpty())
// {
// LogHelper.Warn($"{floor} 层没有空余库位");
// return null;
// }
// wareHouses = wareHouses.OrderBy(p => p.AreaNumSort).OrderBy(p => p.ReservoirAreaColumnId).ToList();
// List<StorageRackDto> sameBatchWarehouses = wareHouses.Where(p => p.MateriaBatch == materiaBatch).ToList();
// StorageRackDto destStorageRack = null;
// //没有相同批次则任选一个空白的列
// if (sameBatchWarehouses.IsNullOrEmpty())
// {
// var groupByColumn = wareHouses.GroupBy(p => p.ReservoirAreaColumnId);
// foreach (var columnGroup in groupByColumn)
// {
// bool emptyColumn = columnGroup.All(p => !p.IsLock && !p.IsDisabled && string.IsNullOrEmpty(p.BarCode));
// if (emptyColumn)
// {
// destStorageRack = columnGroup.OrderByDescending(p => p.NumSort).First();
// break;
// }
// }
// }
// else //存在相同批次则搜寻该批次所在的列下是否还有空库位
// {
// foreach (StorageRackDto sameBatchWarehous in sameBatchWarehouses)
// {
// var sameColumnWarehouse = wareHouses.Where(p => p.ReservoirAreaColumnId == sameBatchWarehous.ReservoirAreaColumnId);
// //同批次的列中也不能有任何库位被锁定
// if (sameColumnWarehouse.All(p => !p.IsLock && !p.IsDisabled))
// {
// destStorageRack = sameColumnWarehouse
// .OrderByDescending(p => p.NumSort)
// .FirstOrDefault(p => string.IsNullOrEmpty(p.BarCode));
// if (destStorageRack != null)
// break;
// }
// }
// //如果同批次下都没有合适的库位,则需要找空列
// if (destStorageRack == null)
// {
// List<StorageRackDto> remain = wareHouses.Except(sameBatchWarehouses, new StorageRackDtoIEqualityComparer()).ToList();
// if (remain.IsNullOrEmpty())
// {
// LogHelper.Warn("排除相同批次库位后没有剩余可用库位");
// return null;
// }
// var groupByColumn = remain.GroupBy(p => p.ReservoirAreaColumnId);
// foreach (var columnGroup in groupByColumn)
// {
// bool emptyColumn = columnGroup.All(p => !p.IsLock && !p.IsDisabled && string.IsNullOrEmpty(p.BarCode));
// if (emptyColumn)
// {
// destStorageRack = columnGroup.OrderByDescending(p => p.NumSort).First();
// break;
// }
// }
// }
// }
// return destStorageRack;
//}
/// <summary>
/// 根据批次号判断是否存在空余库位
/// </summary>
/// <param name="materialBatch"></param>
/// <returns></returns>
//public async Task<bool> HasEmptyWareHouse(string materiaBatch, TaskTypeEnum taskType, FloorEnum floor)
//{
// //直接调用目标库位计算方法,能够计算出目标库位则有库位,否则就是没有
// //一楼采用完全不混放模式
// //二楼视配置
//}
/// <summary>
/// 获取可用的提升机库位
/// </summary>
/// <param name="floor"></param>
/// <returns></returns>
public async Task<List<StorageRackDto>> GetUsableElevatorWareHouse(FloorEnum floor)
{
List<long?> reservoirAreaIds = floor == FloorEnum.FirstFloor ? [1091, 1092] : [2091, 2092];
List<StorageRackDto> elevatorWarehouse = await
GetListByExpression(p =>
reservoirAreaIds.Contains(p.ReservoirAreaId)
&&
!p.IsLock && !p.IsDisabled
);
return elevatorWarehouse;
}
/// <summary>
/// 根据提升机编号和通道号获取提所属库位信息
/// </summary>
/// <param name="elevatorNo"></param>
/// <param name="channel"></param>
/// <returns></returns>
public async Task<StorageRackDto> GetElevatorWareHouse(int elevatorNo, int channel)
{
string nameMatch = elevatorNo == 1 ? "1号" : "2号";
StorageRackDto storageRack = await GetSingalByExpression(p => p.ElevatorChannel == channel && p.StorageRackName.Contains(nameMatch));
return storageRack;
}
public async Task<bool> Lock(StorageRackDto storageRack)
{
storageRack.IsLock = true;
LogHelper.Info($"锁定库位{storageRack.StorageRackNo}");
return await base.EditSingalWithSpecificCols(storageRack, ["IsLock"]);
}
public async Task<bool> Unlock(StorageRackDto storageRack)
{
storageRack.IsLock = false;
LogHelper.Info($"解锁库位{storageRack.StorageRackNo}");
return await base.EditSingalWithSpecificCols(storageRack, ["IsLock"]);
}
public Task<bool> UpdateLockStatus(Guid id, bool isLock)
{
return base.ExcuteSql($"update StorageRack set IsLock = {isLock} where Id = '{id}'");
}
public Task<bool> UpdateDisableStatus(Guid id, bool isDisable)
{
return base.ExcuteSql($"update StorageRack set IsDisabled = {isDisable} where Id = '{id}'");
}
private static readonly SemaphoreSlim SemaphoreSlim = new(1,1);
public async Task<StorageRackDto> GetUsableTransferStorageRack()
{
await SemaphoreSlim.WaitAsync();
StorageRackDto storageRackDto = await
base.GetSingalByExpression(p =>
p.Transport == TransportEnum.TransitArea &&
!p.IsLock &&
!p.IsDisabled &&
string.IsNullOrEmpty(p.BarCode));
if (storageRackDto != null)
await Lock(storageRackDto);
SemaphoreSlim.Release();
return storageRackDto;
}
//public async Task<int> PreCalUsableStorageRackCount(FloorEnum floor)
//{
// //1. 一楼不混放,需要查找同批次,或者空白列
// //2.二楼可以混放,仅需要查找到一个空库位即可
//}
}
}
@@ -0,0 +1,19 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Domain.IRepository;
namespace JSMachine.WMS.App.ServiceImpl
{
public class UserInfoService : BaseService<UserInfoDto, UserInfo>, IUserInfoService
{
private IUserInfoRepository _userInfoRepository;
public UserInfoService(IUserInfoRepository repository) : base(repository)
{
_userInfoRepository = repository;
}
// 可以在这里添加UserInfo特有的方法实现
}
}
@@ -0,0 +1,69 @@
using IdentityServer4.Models;
using JSMachine.WMS.Authorization.Model;
namespace JSMachine.WMS.Authorization
{
/// <summary>
/// 从授权配置中解析 IdentityServer 的身份资源、API 作用域和客户端信息。
/// </summary>
public class AuthorizationConfigResolver
{
private static IConfiguration _configuration;
/// <summary>
/// 设置后续解析操作使用的配置对象。
/// </summary>
/// <param name="configuration">包含 AppSettings 配置节的配置对象。</param>
public static void SetConfig(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// 获取 OpenID Connect 所需的标准身份资源。
/// </summary>
public static IEnumerable<IdentityResource> IdentityResources =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
/// <summary>
/// 从 AppSettings:ApiScopes 读取 API 作用域配置。
/// </summary>
/// <returns>配置存在时返回 API 作用域集合,否则返回空值。</returns>
public static IEnumerable<ApiScope>? GetApiScopes()
{
return _configuration
.GetSection("AppSettings")
.GetSection("ApiScopes")
.Get<string[]>()
?.Select(p => new ApiScope(p));
}
/// <summary>
/// 从 AppSettings:Clients 读取客户端配置,并转换为客户端凭据模式定义。
/// </summary>
/// <returns>配置存在时返回客户端集合,否则返回空值。</returns>
public static IEnumerable<Client>? GetClients()
{
List<ClientInfo> clients = _configuration
.GetSection("AppSettings")
.GetSection("Clients")
.Get<List<ClientInfo>>();
return clients?.Select(p => new Client
{
ClientId = p.ClientId,
//用于身份验证的密钥
ClientSecrets = new Secret[] { new Secret(p.Secret.Sha256()) },
//客户端有权访问的范围
AllowedScopes = p.AllowedScopes,
//过期时间(秒)
AccessTokenLifetime = p.AccessTokenLifetime,
//没有交互式用户,使用 clientid/secret 进行身份验证
AllowedGrantTypes = GrantTypes.ClientCredentials
});
}
}
}
@@ -0,0 +1,31 @@
namespace JSMachine.WMS.Authorization
{
/// <summary>
/// 授权服务宿主启动入口,负责创建并运行 ASP.NET Core 主机。
/// </summary>
public static class AuthorizationEngine
{
/// <summary>
/// 加载授权服务配置、构建 Web 主机并阻塞运行服务。
/// </summary>
public static void Start()
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath($"{hostingContext.HostingEnvironment.ContentRootPath}")
.AddJsonFile("appSettingsAuthorization.json", false, true)
.AddEnvironmentVariables();
})
.Build()
.Run();
}
}
}
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>AnyCPU;x64</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="4.1.2" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<NameOfLastUsedPublishProfile>F:\Gitfactory\yzzx_new\papermes1.0\JSMachine.WMS.Authorization\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
</PropertyGroup>
</Project>
@@ -0,0 +1,10 @@
namespace JSMachine.WMS.Authorization.Model
{
public class ClientInfo
{
public string ClientId { get; set; }
public string Secret { get; set; }
public List<string> AllowedScopes { get; set; }
public int AccessTokenLifetime { get; set; }
}
}
@@ -0,0 +1,29 @@
using JSMachine.WMS.Authorization;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var config = builder.Configuration;
AuthorizationConfigResolver.SetConfig(config);
builder.Services.AddControllers();
builder.Services
.AddIdentityServer()
.AddDeveloperSigningCredential() //���������û��֤�����ʹ�õĿ���������
.AddInMemoryApiScopes(AuthorizationConfigResolver.GetApiScopes())
.AddInMemoryClients(AuthorizationConfigResolver.GetClients())
.AddInMemoryIdentityResources(AuthorizationConfigResolver.IdentityResources);
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseIdentityServer();
app.UseAuthorization();
app.MapControllers();
app.Run();
@@ -0,0 +1,31 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:42074",
"sslPort": 0
}
},
"profiles": {
"JSMachine.PrintDCS.Authorization": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5199",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,52 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
namespace JSMachine.WMS.Authorization
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
AuthorizationConfigResolver.SetConfig(Configuration);
services
.AddIdentityServer()
.AddDeveloperSigningCredential() //���������û��֤�����ʹ�õĿ���������
.AddInMemoryApiScopes(AuthorizationConfigResolver.GetApiScopes())
.AddInMemoryClients(AuthorizationConfigResolver.GetClients())
.AddInMemoryIdentityResources(AuthorizationConfigResolver.IdentityResources);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"urls": "http://127.0.0.1:8077",
"AppSettings": {
"ApiScopes": [ "PostOrderApi" ],
"Clients": [
{
"ClientId": "用友测试用户",
"Secret": "123456",
"AllowedScopes": [ "PostOrderApi" ],
"AccessTokenLifetime": 3600
}
]
}
}
@@ -0,0 +1,52 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Business.Common.Models;
namespace JSMachine.WMS.Business.Common.IService
{
/// <summary>
/// WMS 公共业务服务,协调库位、ERP 任务和 AGV 任务之间的调度关系。
/// </summary>
public interface ICommonService
{
/// <summary>
/// 库位所在列是否被锁定(有待完成的任务)
/// </summary>
/// <param name="storageRack"></param>
/// <returns></returns>
Task<bool> IsColLock(StorageRackDto storageRack);
/// <summary>
/// 跟据erp任务获取已经生成的agv任务列表(不包含已经取消的任务)
/// </summary>
/// <param name="erpTask"></param>
/// <returns></returns>
Task<List<AGVTaskDto>> GetCreatedAgvTasksFromErpTask(ERPTaskDto erpTask,TaskTypeEnum taskType);
/// <summary>
/// 生成中转任务。指定起点,终点为中转区
/// </summary>
/// <param name="startPosition"></param>
/// <returns></returns>
Task<bool> CreateAgvTaskToTransferArea(StorageRackDto startPosition, TaskTypeEnum taskType, ERPTaskDto erpTask);
/// <summary>
/// 中转区作业调度 起点为中转区域 终点需计算
/// </summary>
/// <param name="startPosition"></param>
/// <returns></returns>
Task<bool> CreateAgvTaskFromTransferArea(StorageRackDto startPosition, AGVTaskDto lastAgvTaskRelated, ERPTaskDto erpTask);
/// <summary>
/// 生成agv任务并插入数据库
/// </summary>
/// <param name="startPosition"></param>
/// <param name="endPosition"></param>
/// <param name="erpTask"></param>
/// <returns></returns>
Task CreateAndInsertAgvTask(AgvTaskCreationParam agvTaskCreationParam);
/// <summary>
/// 获取可用提升机库位
/// </summary>
/// <param name="floor">楼层</param>
/// <param name="taskType">任务类型 1-入库 2-出库</param>
/// <returns></returns>
Task<StorageRackDto> GetUsableElevator(FloorEnum floor, int taskType);
}
}
@@ -0,0 +1,41 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.Common.Models
{
/// <summary>
/// AGV任务创建参数模型
/// </summary>
public class AgvTaskCreationParam
{
/// <summary>
/// 起点
/// </summary>
public StorageRackDto StartPosition { get; set; }
/// <summary>
/// 终点
/// </summary>
public StorageRackDto EndPosition { get; set; }
/// <summary>
/// 关联的erp任务
/// </summary>
public Guid ErpTaskId { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public TaskTypeEnum TaskType { get; set; }
/// <summary>
/// 最终出库码头(仅针对出库有效)
/// </summary>
public TransportEnum StockOutWharf { get; set; }
/// <summary>
/// erp上传状态
/// </summary>
public ErpUploadStatusEnum ErpUploadStatus { get; set; }
}
}
@@ -0,0 +1,301 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.Common.IService;
using JSMachine.WMS.Business.Common.Models;
using JSMachine.WMS.Business.SSE;
using JSMachine.WMS.Business.StockIn.IService;
using JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using System.Diagnostics;
namespace JSMachine.WMS.Business.Common.ServiceImpl
{
/// <summary>
/// WMS 通用业务服务,协调 ERP 任务、库位和 AGV 任务,
/// 负责中转区及提升机相关任务的生成。
/// </summary>
public class CommonService : ICommonService
{
private IERPTaskService _erpTaskService;
private IStorageRackService _storageRackService;
private IAGVTaskService _agvTaskService;
public CommonService(
IERPTaskService erpTaskService,
IStorageRackService storageRackService,
IAGVTaskService agvTaskService)
{
_erpTaskService = erpTaskService;
_storageRackService = storageRackService;
_agvTaskService = agvTaskService;
}
/// <summary>
/// 检查指定库位所在列是否存在锁定库位。
/// 该判断通过查询同列全部库位完成,任一库位被锁定即视为整列锁定。
/// </summary>
/// <param name="storageRack">用于确定库位列的库位信息。</param>
/// <returns>同列存在锁定库位时返回 <see langword="true"/>。</returns>
public async Task<bool> IsColLock(StorageRackDto storageRack)
{
List<StorageRackDto> storageRacksThisCol = await _storageRackService
.GetListByExpression(p => p.ReservoirAreaColumnId == storageRack.ReservoirAreaColumnId);
if (storageRacksThisCol.IsNullOrEmpty())
return false;
if (storageRacksThisCol.Any(p => p.IsLock))
return true;
return false;
}
/// <summary>
/// 跟据erp任务获取已经生成的agv任务列表(不包含已经取消的任务)
/// </summary>
/// <param name="materialBatch"></param>
/// <returns></returns>
public async Task<List<AGVTaskDto>> GetCreatedAgvTasksFromErpTask(ERPTaskDto erpTask, TaskTypeEnum taskType)
{
List<AGVTaskDto> agvTasks = await _agvTaskService
.GetListByExpression(p => p.ErpTaskId == erpTask.Id &&
p.TaskType == taskType &&
p.TaskStatus != TaskStatusEnum.Cancelled &&
p.TaskStatus != TaskStatusEnum.Failed);
agvTasks = agvTasks
.GroupBy(p => p.BarCode)
.Select(p => p.First())
.ToList();
return agvTasks;
}
/// <summary>
/// 生成到中转区域的任务。指定起点,终点为中转区
/// </summary>
/// <param name="startPosition"></param>
/// <returns></returns>
public async Task<bool> CreateAgvTaskToTransferArea(StorageRackDto startPosition, TaskTypeEnum taskType, ERPTaskDto erpTask)
{
StorageRackDto endPosition = await _storageRackService.GetUsableTransferStorageRack();
if (endPosition == null)
{
LogHelper.Info("中转区域库位已满");
return false;
}
await CreateAndInsertAgvTask(new AgvTaskCreationParam
{
StartPosition = startPosition,
EndPosition = endPosition,
ErpTaskId = erpTask.Id,
TaskType = taskType,
StockOutWharf = erpTask.Transport,
ErpUploadStatus = ErpUploadStatusEnum.Unnecessary
});
return true;
}
/// <summary>
/// 中转区作业调度 起点为中转区域 终点需计算
/// </summary>
/// <param name="startPosition">要调度的中转区物料</param>
/// <returns></returns>
public async Task<bool> CreateAgvTaskFromTransferArea(StorageRackDto startPosition, AGVTaskDto lastAgvTaskRelated, ERPTaskDto erpTask)
{
// 中转区任务按出库、入库、清理出库阻塞物的顺序生成;任一环节失败即停止后续调度。
bool bRet = await CreateTransferStockOutAgvTask(startPosition, lastAgvTaskRelated, erpTask);
if (!bRet)
return false;
bRet = await CreateTransferStockInAgvTask(startPosition, lastAgvTaskRelated, erpTask);
if (!bRet)
return false;
bRet = await CreateRemoveStockOutObstructionAgvTask(startPosition, lastAgvTaskRelated, erpTask);
if (!bRet)
return false;
return true;
}
private async Task<bool> CreateTransferStockOutAgvTask(
StorageRackDto startPosition,
AGVTaskDto lastAgvTaskRelated,
ERPTaskDto erpTask)
{
if (lastAgvTaskRelated.TaskType != TaskTypeEnum.TransferStockOut)
return true;
StorageRackDto elevator = await GetUsableElevator(FloorEnum.SecondFloor, 2);
if (elevator == null)
{
LogHelper.Warn($"中转区出库业务 所有提升机出库库位均被WMS或者PLC锁定,erpTask {lastAgvTaskRelated.ErpTaskId} 无法生成AgvTask");
return false;
}
await CreateAndInsertAgvTask(new AgvTaskCreationParam
{
StartPosition = startPosition,
EndPosition = elevator,
ErpTaskId = erpTask.Id,
TaskType = TaskTypeEnum.TransferStockOut,
StockOutWharf = erpTask.Transport,
ErpUploadStatus = ErpUploadStatusEnum.Unnecessary
});
return true;
}
private async Task<bool> CreateTransferStockInAgvTask(StorageRackDto startPosition, AGVTaskDto lastAgvTaskRelated, ERPTaskDto erpTask)
{
if (lastAgvTaskRelated.TaskType != TaskTypeEnum.TransferStockIn)
return true;
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(
TransportEnum.TransitArea,
FloorEnum.SecondFloor,
startPosition.MateriaBatch);
StorageRackDto endPosition = await stockInStrategy.CalCulateWarehouse();
if (endPosition == null)
return false;
await CreateAndInsertAgvTask(new AgvTaskCreationParam
{
StartPosition = startPosition,
EndPosition = endPosition,
ErpTaskId = erpTask.Id,
TaskType = lastAgvTaskRelated.TaskType,
ErpUploadStatus =
(erpTask.TaskType == TaskTypeEnum.PdaIn || erpTask.TaskType == TaskTypeEnum.TailIn) ?
ErpUploadStatusEnum.WaittingToUpload :
ErpUploadStatusEnum.Unnecessary
});
return true;
}
private async Task<bool> CreateRemoveStockOutObstructionAgvTask(
StorageRackDto startPosition,
AGVTaskDto lastAgvTaskRelated,
ERPTaskDto erpTask)
{
if (lastAgvTaskRelated.TaskType != TaskTypeEnum.RemoveStockOutObstruction)
return true;
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(
TransportEnum.TransitArea,
FloorEnum.SecondFloor,
startPosition.MateriaBatch);
StorageRackDto endPosition = await stockInStrategy.CalCulateWarehouse();
if (endPosition == null)
return false;
await CreateAndInsertAgvTask(new AgvTaskCreationParam
{
StartPosition = startPosition,
EndPosition = endPosition,
ErpTaskId = erpTask.Id,
TaskType = lastAgvTaskRelated.TaskType,
ErpUploadStatus =
(erpTask.TaskType == TaskTypeEnum.PdaIn || erpTask.TaskType == TaskTypeEnum.TailIn) ?
ErpUploadStatusEnum.WaittingToUpload :
ErpUploadStatusEnum.Unnecessary
});
return true;
}
/// <summary>
/// 获取可用提升机库位
/// </summary>
/// <param name="floor">楼层</param>
/// <param name="taskType">任务类型 1-入库 2-出库</param>
/// <returns></returns>
public async Task<StorageRackDto> GetUsableElevator(FloorEnum floor, int taskType)
{
List<StorageRackDto> elevatorWarehouse = await _storageRackService.GetListByExpression(p =>
p.Transport == TransportEnum.Elevator &&
p.Floor == floor &&
!p.IsLock
);
if (elevatorWarehouse.IsNullOrEmpty())
{
return null;
}
List<ElevatorPlcStatus> elevatorPlcStatuses = await ElevatorStatusRead.ReadAllElevatorStatus();
StorageRackDto elevator = null;
foreach (StorageRackDto elvatorWarehouse in elevatorWarehouse)
{
int elevatorNo = elvatorWarehouse.StorageRackName.Contains("1号") ? 1 : 2;
ElevatorPlcStatus elevatorPlcStatus = elevatorPlcStatuses.First(p => p.ElevatorNo == elevatorNo);
//入库
if (taskType == 1)
{
if (elvatorWarehouse.ElevatorChannel == 1001 && elevatorPlcStatus.StockInPutdownChanelTwo ||
elvatorWarehouse.ElevatorChannel == 1011 && elevatorPlcStatus.StockInPutdownChanelOne)
{
elevator = elvatorWarehouse;
break;
}
}
//出库
if (taskType == 2)
{
if (elvatorWarehouse.ElevatorChannel == 2001 && elevatorPlcStatus.StockOutPutdownChanelTwo ||
elvatorWarehouse.ElevatorChannel == 2011 && elevatorPlcStatus.StockOutPutdownChanelOne)
{
elevator = elvatorWarehouse;
break;
}
}
}
return elevator;
}
public async Task CreateAndInsertAgvTask(AgvTaskCreationParam agvTaskCreationParam)
{
AGVTaskDto agvTask = new()
{
ErpTaskId = agvTaskCreationParam.ErpTaskId,
BarCode = agvTaskCreationParam.StartPosition.BarCode,
OrderNo = agvTaskCreationParam.StartPosition.OrderNo,
CustomName = agvTaskCreationParam.StartPosition.CustomName,
MateriaName = agvTaskCreationParam.StartPosition.MateriaName,
MateriaType = agvTaskCreationParam.StartPosition.MateriaType,
MateriaNum = agvTaskCreationParam.StartPosition.MateriaNum,
MateriaBatch = agvTaskCreationParam.StartPosition.MateriaBatch,
MateriaCode = agvTaskCreationParam.StartPosition.MateriaCode,
TaskType = agvTaskCreationParam.TaskType,
TaskStatus = TaskStatusEnum.Initial,
Priority = agvTaskCreationParam.TaskType == TaskTypeEnum.SaleOut ? PriorityEnum.High : PriorityEnum.Middle,
Floor = agvTaskCreationParam.StartPosition.Floor,
StartPositionCode = agvTaskCreationParam.StartPosition.StorageRackNo,
EndPositionCode = agvTaskCreationParam.EndPosition.StorageRackNo,
Transport = agvTaskCreationParam.StockOutWharf,
ErpUploadStatus = agvTaskCreationParam.ErpUploadStatus
};
await _storageRackService.Lock(agvTaskCreationParam.StartPosition);
await _storageRackService.Lock(agvTaskCreationParam.EndPosition);
await _agvTaskService.Add(agvTask);
//发送广播通知前端网站刷新页面
SseEngine.BrodCast(agvTaskCreationParam.StartPosition.Floor.ToString());
}
}
}
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<Folder Include="Exstension\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JSMachine.WMS.Common\JSMachine.WMS.Common.csproj" />
<ProjectReference Include="..\JSMachine.WMS.PLC\JSMachine.WMS.PLC.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,25 @@
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using JSMachine.WMS.RPC.RcsRPC.Dto;
using JSMachine.WMS.RPC.ErpRPC.Dto.Out;
using JSMachine.WMS.App.Dto;
namespace JSMachine.WMS.Business.RcsCallBack.IService
{
public interface IRcsCallBackService
{
/// <summary>
/// 给Rcs调用,获取AGV上报的任务状态
/// </summary>
/// <param name="callBackWmsParam"></param>
/// <returns></returns>
Task<RcsApiResult> DealwithExcutionStatus(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask);
/// <summary>
/// 处理PDA上报的agv报警处理结果
/// </summary>
/// <param name="agvTaskId"></param>
/// <returns></returns>
Task<bool> DealWithAlarmHandleResult(Guid agvTaskId);
}
}
@@ -0,0 +1,25 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.IService.StaePattern
{
/// <summary>
/// agv任务状态处理器 状态设计模式
/// </summary>
public interface IAgvTaskStateHandler
{
/// <summary>
/// 处理一次 AGV 状态回调,并按状态推进任务或更新任务结果。
/// </summary>
/// <param name="agvExcutionInfo">RCS 返回的 AGV 执行信息。</param>
/// <param name="agvTask">系统中的 AGV 任务。</param>
/// <param name="destWarehouse">任务目标库位。</param>
/// <returns>状态处理是否成功。</returns>
Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse);
}
}
@@ -0,0 +1,18 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Domain.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.IService.StrategyPattern
{
/// <summary>
/// agv任务结束策略(放货完成即代表agv任务结束)
/// </summary>
public interface ITaskCompletionStrategy
{
Task<bool> CompleteTaskAsync(AGVTaskDto agvTask, StorageRackDto destWarehouse);
}
}
@@ -0,0 +1,419 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.RcsCallBack.IService;
using JSMachine.WMS.Business.RcsCallBack.IService.StaePattern;
using JSMachine.WMS.Business.RcsCallBack.IService.StrategyPattern;
using JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern;
using JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StrategyPatternImpl;
using JSMachine.WMS.Common.Dto.Business;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.RPC.RcsRPC.Dto;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl
{
/// <summary>
/// RCS 回调业务,将外部 AGV 状态转换为本地任务、库存及报警状态。
/// </summary>
public class RcsCallBackService : IRcsCallBackService
{
private IAGVTaskService _agvTaskService;
private IElevatorPlcQueueService _elevatorPlcQueueService;
private IStorageRackService _storageRackService;
private IERPTaskService _erptaskService;
private IElevatorCountService _elevatorCountService;
public RcsCallBackService(
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService,
IStorageRackService storageRackService,
IERPTaskService erpTaskService,
IElevatorCountService elevatorCountService)
{
_agvTaskService = agvTaskService;
_elevatorPlcQueueService = elevatorPlcQueueService;
_storageRackService = storageRackService;
_erptaskService = erpTaskService;
_elevatorCountService = elevatorCountService;
}
/// <summary>
/// 给Rcs调用,获取AGV上报的任务状态
/// </summary>
/// <param name="callBackWmsParam"></param>
/// <returns></returns>
public async Task<RcsApiResult> DealwithExcutionStatus(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
// 先处理失败和取消状态,再处理成功状态;各处理器根据回调状态自行决定是否生效。
//处理agv执行失败的回调,推送pda报警
await HandleFailed(agvExcutionInfo, agvTask);
await HandleCanceled(agvExcutionInfo, agvTask);
//处理agv执行成功的回调
RcsApiResult rcsApiResult = await HandleSuccess(agvExcutionInfo, agvTask);
return rcsApiResult;
}
public async Task<bool> DealWithAlarmHandleResult(Guid agvTaskId)
{
AGVTaskDto agvTask = await _agvTaskService.GetSingalByExpression(p => p.Id == agvTaskId);
if (agvTask == null)
{
LogHelper.Warn($"PDA上传的 {agvTaskId} 任务不存在,该任务告警可能已经处理?");
return true;
}
//模拟rcs回调,发送放货完成命令
await DealwithExcutionStatus(new AgvExcutionInfo
{
OrderId = agvTaskId.ToString(),
Status = 23
}, agvTask);
//处理完删除缓存中的报警信息
GlobalAgvTaskFailedAlarmInfo.RemoveAlarmInfo(agvTaskId);
return true;
}
#region 处理入库rcs回调信息
private async Task HandleCanceled(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (agvExcutionInfo.Status != 3)
return;
agvTask.TaskStatus = TaskStatusEnum.Cancelled;
await _agvTaskService.EditSingal(agvTask);
//搬运的任务没有对应的erptask
if (agvTask.TaskType != TaskTypeEnum.Carry)
{
ERPTaskDto erpTask = await _erptaskService.GetSingalByExpression(p => p.Id == agvTask.ErpTaskId);
erpTask.TaskStatus = TaskStatusEnum.Cancelled;
await _erptaskService.UpdateTaskStatus(erpTask);
}
}
private async Task HandleFailed(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (agvExcutionInfo.Status != 7)
return;
//1.一楼 起点在入库库位,终点在提升机,人工搬运托盘到提升机入口,手动提交放货完成信号
//2.二楼 起点在提升机出口,终点在存储区库位,人工搬运托盘到库位,手动提交放货完成信号
//3.二楼 起点在入库区库位,终点在提升机入口,人工搬运托盘到提升机入口,手动提交放货完成信号
//4.一楼 起点在提升机,终点在出货码头,人工搬运托盘到出货码头,无需其他处理
await HandleStocInFirstFloor(agvExcutionInfo, agvTask);
await HandleStocInSecondFloor(agvExcutionInfo, agvTask);
await HandleStocOutSecondFloor(agvExcutionInfo, agvTask);
await HandleStocOutFirstFloor(agvExcutionInfo, agvTask);
}
#region agv执行失败报警分任务处理
private async Task HandleStocInFirstFloor(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (!(agvTask.TaskType == TaskTypeEnum.PdaIn && agvTask.Floor == FloorEnum.FirstFloor))
return;
StorageRackDto destWarehouse = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == agvTask.EndPositionCode);
//终点在存储区,人工搬运获取到指定库位,完成后写放货完成
if (destWarehouse.Transport == TransportEnum.NormalArea)
{
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从车上取下搬运到 {destWarehouse.StorageRackName} " +
$"库区的 {destWarehouse.StorageRackNo}库位上,完成后点击已处理按钮";
LogHelper.Warn(operatn);
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
await UpdateErpTaskStatus(agvTask, TaskStatusEnum.Failed);
}
//终点在提升机入口,人工搬运获取到提升机入口,完成后写放货完成
if (destWarehouse.Transport == TransportEnum.Elevator)
{
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从车上(或库位{agvTask.StartPositionCode})搬运到 {destWarehouse.StorageRackName} 上," +
$"完成后点击已处理按钮";
LogHelper.Warn($"任务 id {agvTask.Id} 操作信息:{operatn}");
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
}
}
private async Task HandleStocInSecondFloor(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (!(agvTask.TaskType == TaskTypeEnum.PdaIn && agvTask.Floor == FloorEnum.SecondFloor))
return;
StorageRackDto startWarehouse = await _storageRackService.GetByStorageRackNo(agvTask.StartPositionCode);
StorageRackDto destWarehouse = await _storageRackService.GetByStorageRackNo(agvTask.EndPositionCode);
//终点在存储区,人工搬运获取到指定库位,完成后写放货完成
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从 {startWarehouse.StorageRackName} 搬运到 " +
$"{destWarehouse.StorageRackNo}库位上,完成后点击已处理按钮";
LogHelper.Warn($"任务 id {agvTask.Id} 操作信息:{operatn}");
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
await UpdateErpTaskStatus(agvTask, TaskStatusEnum.Failed);
}
private async Task HandleStocOutSecondFloor(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (!(agvTask.TaskType == TaskTypeEnum.SaleOut && agvTask.Floor == FloorEnum.SecondFloor))
return;
StorageRackDto destWarehouse = await _storageRackService.GetByStorageRackNo(agvTask.EndPositionCode);
//终点在存储区,人工搬运获取到指定库位,完成后写放货完成
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从车上(或者{agvTask.StartPositionCode})放到 {destWarehouse.StorageRackNo} 上,完成后点击已处理按钮";
LogHelper.Warn($"任务 id {agvTask.Id} 操作信息:{operatn}");
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
}
private async Task HandleStocOutFirstFloor(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (!(agvTask.TaskType == TaskTypeEnum.SaleOut && agvTask.Floor == FloorEnum.SecondFloor))
return;
StorageRackDto startWarehouse = await _storageRackService.GetByStorageRackNo(agvTask.StartPositionCode);
StorageRackDto destWarehouse = await _storageRackService.GetByStorageRackNo(agvTask.EndPositionCode);
if (startWarehouse.Transport == TransportEnum.Elevator)
{
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从 {startWarehouse.StorageRackName} 搬运到 " +
$"{(destWarehouse.Transport == TransportEnum.NorthArea ? "北出库码头" : "南出库码头")} " +
$"的 {destWarehouse.StorageRackNo} 库位上,完成后点击已处理按钮";
LogHelper.Warn($"任务 id {agvTask.Id} 操作信息:{operatn}");
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
}
if (startWarehouse.Transport == TransportEnum.NorthArea)
{
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从 " +
$"{startWarehouse} 库位上搬运到北出库码头的" +
$"{destWarehouse.StorageRackNo} 库位上,完成后点击已处理按钮";
LogHelper.Warn($"任务 id {agvTask.Id} 操作信息:{operatn}");
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
}
if (startWarehouse.Transport == TransportEnum.SouthArea)
{
string operatn = $"{agvExcutionInfo.DeviceNum}号小车报警,请将货物从" +
$"{startWarehouse} 库位搬运到南出库码头" +
$"的 {destWarehouse.StorageRackNo} 库位上,完成后点击已处理按钮";
LogHelper.Warn($"任务 id {agvTask.Id} 操作信息:{operatn}");
GlobalAgvTaskFailedAlarmInfo.AddAlarmInfo(new AgvTaskFailedAlarmInfo
{
AgvTaskId = agvTask.Id,
Operation = operatn
});
}
}
#endregion
private async Task<RcsApiResult> HandleSuccess(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask)
{
if (!new int[] { 21, 23 }.Contains(agvExcutionInfo.Status))
{
LogHelper.Warn("rcs回调状态不是21,23,不处理");
return new RcsApiResult { code = 1000 };
}
StorageRackDto destWarehouse = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == agvTask.EndPositionCode);
if (destWarehouse == null)
{
LogHelper.Error($"AgvTask终点库位 {agvTask.EndPositionCode} 不存在");
return new RcsApiResult { code = 1000 }; ;
}
IAgvTaskStateHandler handler = new RcsCallbackHandlerFactory().CreateHandler(agvExcutionInfo.Status, destWarehouse);
if (handler == null)
{
LogHelper.Error("状态模式 没有匹配的模式可以处理");
return new RcsApiResult { code = 1000 };
}
await handler.HandleAsync(agvExcutionInfo, agvTask, destWarehouse);
return new RcsApiResult { code = 1000 };
}
private async Task UpdateErpTaskStatus(AGVTaskDto agvTask, TaskStatusEnum taskStatus)
{
ERPTaskDto erpTask = await _erptaskService.GetSingalByExpression(p => p.Id == agvTask.ErpTaskId);
erpTask.TaskStatus = taskStatus;
//更新erp任务状态为完成
bool bRet = await _erptaskService.EditSingal(erpTask);
LogHelper.Info($"更新erptask状态为 {taskStatus} ,数据库操作结果 {bRet}");
}
#endregion
#region 处理销售出库/生产退库,Rcs回调
/// <summary>
/// 解锁中区库位并绑定合格证
/// </summary>
/// <returns></returns>
private async Task UnlockAndSetMiddleWarehouse(StorageRackDto storageRack)
{
if (storageRack.Transport != TransportEnum.MiddleOutArea)
return;
storageRack.IsLock = false;
await _storageRackService.EditSingalWithSpecificCols(storageRack, ["IsLock", "BarCode"]);
}
/// <summary>
/// 更新AgvTask任务状态
/// </summary>
/// <param name="agvTask">库位编号</param>
/// <returns></returns>
private async Task<bool> UpdateAgvTaskStatus(AGVTaskDto agvTask)
{
var updateResult = false;
if (agvTask != null)
{
updateResult = await _agvTaskService.EditSingal(agvTask);
if (!updateResult)
{
LogHelper.Error($"销售出库:更新AgvTask {agvTask.Id} 的,完成状态失败");
}
else
{
LogHelper.Info($"销售出库:更新AgvTask {agvTask.Id} 的,完成状态成功");
}
}
return updateResult;
}
/// <summary>
/// 清空起点库位上的物料信息
/// </summary>
/// <param name="agvTask"></param>
/// <returns></returns>
private async Task<bool> ClearMateriaInfo(AGVTaskDto agvTask)
{
var clearResult = false;
var typeMsg = "";
if (agvTask.TaskType == TaskTypeEnum.SaleOut)
{
typeMsg = "销售出库,";
}
else if (agvTask.TaskType == TaskTypeEnum.Carry)
{
typeMsg = "搬运物料,";
}
var clearWarehouse = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == agvTask.StartPositionCode);
if (clearWarehouse != null)
{
clearWarehouse.MateriaBatch = "";
clearWarehouse.MateriaNum = 0;
clearWarehouse.OrderNo = "";
clearWarehouse.MateriaName = "";
clearWarehouse.MateriaType = "";
clearWarehouse.MateriaCode = "";
clearWarehouse.BarCode = "";
clearWarehouse.IsLock = false;
clearResult = await _storageRackService.EditSingal(clearWarehouse);
if (!clearResult)
{
LogHelper.Error($"{typeMsg}清空库位{clearWarehouse.StorageRackNo}上的物料信息{clearWarehouse.MateriaCode}失败");
}
else
{
LogHelper.Info($"{typeMsg}清空库位{clearWarehouse.StorageRackNo}上的物料信息{clearWarehouse.MateriaCode}成功");
}
}
else
{
LogHelper.Error($"{typeMsg}清空库位时,未查询到库位{agvTask.StartPositionCode}上的物料信息");
}
return clearResult;
}
/// <summary>
/// 更新终点库位上的物料信息
/// </summary>
/// <param name="agvTask"></param>
/// <returns></returns>
private async Task<bool> UpdateMateriaInfo(AGVTaskDto agvTask)
{
var updateResult = false;
var updateWarehouse = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == agvTask.EndPositionCode);
if (updateWarehouse != null)
{
updateWarehouse.MateriaBatch = agvTask.MateriaBatch;
updateWarehouse.MateriaNum = agvTask.MateriaNum;
updateWarehouse.OrderNo = agvTask.OrderNo;
updateWarehouse.MateriaName = agvTask.MateriaName;
updateWarehouse.MateriaType = agvTask.MateriaType;
updateWarehouse.MateriaCode = agvTask.MateriaCode;
updateWarehouse.BarCode = agvTask.BarCode;
updateWarehouse.IsLock = false;
updateResult = await _storageRackService.EditSingal(updateWarehouse);
if (!updateResult)
{
LogHelper.Error($"销售出库:更新库位{updateWarehouse.StorageRackNo}上的物料信息{updateWarehouse.MateriaCode}失败");
}
else
{
LogHelper.Info($"销售出库:更新库位{updateWarehouse.StorageRackNo}上的物料信息{updateWarehouse.MateriaCode}成功");
}
}
else
{
LogHelper.Error($"销售出库:更新库位时,未查询到库位{agvTask.EndPositionCode}上的物料信息");
}
return updateResult;
}
#endregion
}
}
@@ -0,0 +1,39 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.RcsCallBack.IService.StaePattern;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern
{
/// <summary>
/// agv任务执行失败处理器
/// </summary>
public class FailedStateHandler : IAgvTaskStateHandler
{
private readonly IAGVTaskService _agvTaskService;
/// <summary>创建失败状态处理器。</summary>
/// <param name="agvTaskService">AGV 任务持久化服务。</param>
public FailedStateHandler(IAGVTaskService agvTaskService)
{
_agvTaskService=agvTaskService;
}
/// <summary>将当前 AGV 任务标记为失败并保存状态。</summary>
/// <param name="agvExcutionInfo">RCS 执行信息。</param>
/// <param name="agvTask">待更新的 AGV 任务。</param>
/// <param name="destWarehouse">目标库位。</param>
/// <returns>状态更新是否成功。</returns>
public async Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
agvTask.TaskStatus = TaskStatusEnum.Failed;
return await _agvTaskService.UpdateAgvTaskStatus(agvTask);
}
}
}
@@ -0,0 +1,92 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.RcsCallBack.IService.StaePattern;
using JSMachine.WMS.Business.SSE;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern
{
/// <summary>
/// agv取货完成处理器
/// </summary>
public class PickupCompletedStateHandler : IAgvTaskStateHandler
{
private readonly IStorageRackService _storageRackService;
private readonly IAGVTaskService _agvTaskService;
public PickupCompletedStateHandler(IStorageRackService storageRackService, IAGVTaskService agvTaskService)
{
_storageRackService = storageRackService;
_agvTaskService = agvTaskService;
}
public async Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
// 清空出库库位上的物料、解锁库位
await ClearMateriaInfo(agvTask);
//更新AgvTask的状态为 取货完成
agvTask.TaskStatus = TaskStatusEnum.PickupCompleted;
return await _agvTaskService.UpdateAgvTaskStatus(agvTask);
}
/// <summary>
/// 清空起点库位上的物料信息
/// </summary>
/// <param name="agvTask"></param>
/// <returns></returns>
private async Task<bool> ClearMateriaInfo(AGVTaskDto agvTask)
{
var clearResult = false;
var typeMsg = "";
if (agvTask.TaskType == TaskTypeEnum.SaleOut)
{
typeMsg = "销售出库,";
}
else if (agvTask.TaskType == TaskTypeEnum.Carry)
{
typeMsg = "搬运物料,";
}
var clearWarehouse = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == agvTask.StartPositionCode);
if (clearWarehouse != null)
{
clearWarehouse.MateriaBatch = "";
clearWarehouse.MateriaNum = 0;
clearWarehouse.OrderNo = "";
clearWarehouse.MateriaName = "";
clearWarehouse.MateriaType = "";
clearWarehouse.MateriaCode = "";
clearWarehouse.BarCode = "";
clearWarehouse.IsLock = false;
clearResult = await _storageRackService.EditSingal(clearWarehouse);
if (!clearResult)
{
LogHelper.Error($"{typeMsg}清空库位{clearWarehouse.StorageRackNo}上的物料信息{clearWarehouse.MateriaCode}失败");
}
else
{
LogHelper.Info($"{typeMsg}清空库位{clearWarehouse.StorageRackNo}上的物料信息{clearWarehouse.MateriaCode}成功");
}
}
else
{
LogHelper.Error($"{typeMsg}清空库位时,未查询到库位{agvTask.StartPositionCode}上的物料信息");
}
//发送广播通知前端网站刷新页面
SseEngine.BrodCast(agvTask.Floor.ToString());
return clearResult;
}
}
}
@@ -0,0 +1,104 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.SSE;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using JSMachine.WMS.PLC.ModbusImpl.PaperFeedGroundRack.ModbusIO.Write;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern.PutdownCompleted
{
/// <summary>
/// 提升机处理器
/// </summary>
public class ElevatorDeliveryHandler : PutdownCompletedStateHandler
{
public ElevatorDeliveryHandler
(IStorageRackService storageRackService,
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService,
IElevatorCountService elevatorCountService,
IERPTaskService erpTaskService) :
base(storageRackService, agvTaskService, elevatorPlcQueueService, elevatorCountService, erpTaskService)
{
}
public async override Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
var elevatorPlcStatus = new ElevatorPlcStatus();
//2.1 写PLC放货完成
int elevatorNo = destWarehouse.StorageRackName.Contains("1号") ? 1 : 2;
elevatorPlcStatus.ElevatorNo = elevatorNo;
if (destWarehouse.ElevatorChannel == 1001)
{
elevatorPlcStatus.StockInPutdownChanelTwo = true;
}
if (destWarehouse.ElevatorChannel == 1011)
{
elevatorPlcStatus.StockInPutdownChanelOne = true;
}
if (destWarehouse.ElevatorChannel == 2001)
{
elevatorPlcStatus.StockOutPutdownChanelTwo = true;
}
if (destWarehouse.ElevatorChannel == 2011)
{
elevatorPlcStatus.StockOutPutdownChanelOne = true;
}
bool bRet = await ElevatorWrite.WriteElevator(elevatorPlcStatus);
if (!bRet)
{
LogHelper.Error($"{agvTask.TaskType.GetDescription()}: " +
$"向 {elevatorPlcStatus.ElevatorNo} 号提升机 {destWarehouse.ElevatorChannel} 网带写放货完成信号失败");
return false;
}
if(agvTask.TaskType!=TaskTypeEnum.Carry)
{
//2.2 写PLC队列信息
ElevatorPlcQueueDto elevatorPlcQueue = new()
{
ElevatorNo = elevatorNo,
ElevatorChannel = destWarehouse.ElevatorChannel.Value,
AgvTaskId = agvTask.Id,
IsLock = false
};
bRet = await _elevatorPlcQueueService.Add(elevatorPlcQueue);
if (!bRet)
{
LogHelper.Error($"{agvTask.TaskType.GetDescription()}:写PLC队列失败");
return false;
}
else
{
LogHelper.Info($"{agvTask.TaskType.GetDescription()}:" +
$"写PLC队列成功,提升机编号 {elevatorPlcQueue.ElevatorNo} 通道号 {elevatorPlcQueue.ElevatorChannel}");
}
//提升机计数
await _elevatorCountService.IncrementCount(elevatorNo, elevatorPlcQueue.ElevatorChannel);
}
//2.3 更新agvtask的状态为完成
await base.CompleteTask(agvTask);
//2.4 解锁提升机库位
await _storageRackService.Unlock(destWarehouse);
//发送广播通知前端网站刷新页面
SseEngine.BrodCast(agvTask.Floor.ToString());
return true;
}
}
}
@@ -0,0 +1,49 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.RcsCallBack.IService.StaePattern;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern.PutdownCompleted
{
/// <summary>
/// 放货完成处理器
/// </summary>
public abstract class PutdownCompletedStateHandler : IAgvTaskStateHandler
{
protected readonly IStorageRackService _storageRackService;
protected readonly IAGVTaskService _agvTaskService;
protected readonly IElevatorPlcQueueService _elevatorPlcQueueService;
protected readonly IElevatorCountService _elevatorCountService;
protected readonly IERPTaskService _erpTaskService;
public PutdownCompletedStateHandler(
IStorageRackService storageRackService,
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService,
IElevatorCountService elevatorCountService,
IERPTaskService erpTaskService
)
{
_storageRackService = storageRackService;
_agvTaskService = agvTaskService;
_elevatorPlcQueueService = elevatorPlcQueueService;
_elevatorCountService= elevatorCountService;
_erpTaskService = erpTaskService;
}
public abstract Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse);
protected async Task CompleteTask(AGVTaskDto agvTask)
{
agvTask.TaskStatus = TaskStatusEnum.Completed;
await _agvTaskService.UpdateAgvTaskStatus(agvTask);
}
}
}
@@ -0,0 +1,75 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.Common.IService;
using JSMachine.WMS.Business.RcsCallBack.IService.StaePattern;
using JSMachine.WMS.Business.SSE;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static JSMachine.WMS.App.Dto.StorageRackDto;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern.PutdownCompleted
{
/// <summary>
/// 存储区(包括中转区)放货完成处理器
/// </summary>
public class StockAreaHandler : PutdownCompletedStateHandler
{
public StockAreaHandler(
IStorageRackService storageRackService,
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService,
IElevatorCountService elevatorCountService,
IERPTaskService erpTaskService) :
base(storageRackService, agvTaskService, elevatorPlcQueueService, elevatorCountService, erpTaskService)
{
}
public async override Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
//1.将物料绑定到目标库位
//2.解锁目标库位
//3.更改agv任务为完成
destWarehouse.BingdingMaterial(new MateriaInfo
{
BarCode = agvTask.BarCode,
MateriaBatch = agvTask.MateriaBatch,
MateriaCode = agvTask.MateriaCode,
MateriaName = agvTask.MateriaName,
MateriaNum = agvTask.MateriaNum.ToString(),
MateriaType = agvTask.MateriaType,
OrderNo = agvTask.OrderNo
});
destWarehouse.IsLock = false;
await _storageRackService.EditSingal(destWarehouse);
await CompleteTask(agvTask);
//更新erp任务为完成状态
if (agvTask.TaskType == TaskTypeEnum.PdaIn ||
agvTask.TaskType == TaskTypeEnum.TailIn ||
agvTask.TaskType == TaskTypeEnum.TransferStockIn)
{
ERPTaskDto erpTask = await _erpTaskService.GetSingalByExpression(p => p.Id == agvTask.ErpTaskId);
if (erpTask != null)
{
erpTask.TaskStatus = TaskStatusEnum.Completed;
erpTask.TaskMsg = "";
await _erpTaskService.EditSingalWithSpecificCols(erpTask, ["TaskStatus", "TaskMsg"]);
}
}
//发送广播通知前端网站刷新页面
SseEngine.BrodCast(agvTask.Floor.ToString());
return true;
}
}
}
@@ -0,0 +1,52 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern.PutdownCompleted
{
/// <summary>
/// 出库码头处理器
/// </summary>
public class StockOutWharfHandler : PutdownCompletedStateHandler
{
public StockOutWharfHandler
(IStorageRackService storageRackService,
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService,
IElevatorCountService elevatorCountService,
IERPTaskService erpTaskService) :
base(storageRackService, agvTaskService, elevatorPlcQueueService, elevatorCountService, erpTaskService)
{
}
public async override Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
destWarehouse.BarCode = agvTask.BarCode;
agvTask.TaskStatus = TaskStatusEnum.Completed;
await UnlockAndSetMiddleWarehouse(destWarehouse);
return await _agvTaskService.UpdateAgvTaskStatus(agvTask);
}
/// <summary>
/// 解锁中区库位并绑定合格证
/// </summary>
/// <returns></returns>
private async Task UnlockAndSetMiddleWarehouse(StorageRackDto storageRack)
{
if (storageRack.Transport != TransportEnum.MiddleOutArea)
return;
storageRack.IsLock = false;
await _storageRackService.EditSingalWithSpecificCols(storageRack, ["IsLock", "BarCode"]);
}
}
}
@@ -0,0 +1,55 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.SSE;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static JSMachine.WMS.App.Dto.StorageRackDto;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern.PutdownCompleted
{
public class TransitAreaHandler : PutdownCompletedStateHandler
{
public TransitAreaHandler(
IStorageRackService storageRackService,
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService,
IElevatorCountService elevatorCountService,
IERPTaskService erpTaskService) :
base(storageRackService, agvTaskService, elevatorPlcQueueService, elevatorCountService, erpTaskService)
{
}
public async override Task<bool> HandleAsync(AgvExcutionInfo agvExcutionInfo, AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
//1.将物料绑定到目标库位
//2.解锁目标库位
//3.更改agv任务为完成
destWarehouse.BingdingMaterial(new MateriaInfo
{
BarCode = agvTask.BarCode,
MateriaBatch = agvTask.MateriaBatch,
MateriaCode = agvTask.MateriaCode,
MateriaName = agvTask.MateriaName,
MateriaNum = agvTask.MateriaNum.ToString(),
MateriaType = agvTask.MateriaType,
OrderNo = agvTask.OrderNo
});
destWarehouse.IsLock = false;
await _storageRackService.EditSingal(destWarehouse);
await CompleteTask(agvTask);
//发送广播通知前端网站刷新页面
SseEngine.BrodCast(agvTask.Floor.ToString());
return true;
}
}
}
@@ -0,0 +1,42 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Business.RcsCallBack.IService.StaePattern;
using JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern.PutdownCompleted;
using JSMachine.WMS.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StatePattern
{
/// <summary>
/// rcs回调处理器工厂类
/// </summary>
public class RcsCallbackHandlerFactory
{
/// <summary>
/// 根据 RCS 返回状态和目标库位运输区域选择状态处理器。
/// </summary>
/// <param name="status">RCS 任务状态码。</param>
/// <param name="destWarehouse">任务目标库位。</param>
/// <returns>负责当前状态后续处理的处理器。</returns>
public IAgvTaskStateHandler CreateHandler(int status, StorageRackDto destWarehouse)
{
return status switch
{
21 => GlobalServericeProvidor.GetService<PickupCompletedStateHandler>(),
23 => destWarehouse.Transport switch
{
TransportEnum.NorthArea or TransportEnum.SouthArea or TransportEnum.MiddleOutArea or TransportEnum.ReturnStockArea
=> GlobalServericeProvidor.GetService<StockOutWharfHandler>(),
TransportEnum.NormalArea => GlobalServericeProvidor.GetService<StockAreaHandler>(),
TransportEnum.TransitArea => GlobalServericeProvidor.GetService<TransitAreaHandler>(),
TransportEnum.Elevator => GlobalServericeProvidor.GetService<ElevatorDeliveryHandler>(),
}
};
}
}
}
@@ -0,0 +1,90 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.RcsCallBack.IService.StrategyPattern;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using JSMachine.WMS.PLC.ModbusImpl.PaperFeedGroundRack.ModbusIO.Write;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StrategyPatternImpl
{
public class ElevatorStrategy : ITaskCompletionStrategy
{
private readonly IElevatorPlcQueueService _elevatorPlcQueueService;
private readonly IStorageRackService _storageRackService;
private readonly IAGVTaskService _agvTaskService;
private readonly IElevatorCountService _elevatorCountService;
public ElevatorStrategy(
IElevatorPlcQueueService elevatorPlcQueueService,
IStorageRackService storageRackService,
IElevatorCountService elevatorCountService,
IAGVTaskService agvTaskService)
{
_elevatorPlcQueueService = elevatorPlcQueueService;
_storageRackService = storageRackService;
_elevatorCountService = elevatorCountService;
_agvTaskService = agvTaskService;
}
public async Task<bool> CompleteTaskAsync(AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
ElevatorPlcStatus elevatorPlcStatus = new();
int elevatorNo = destWarehouse.StorageRackName.Contains("1号") ? 1 : 2;
elevatorPlcStatus.ElevatorNo = elevatorNo;
if (destWarehouse.ElevatorChannel == 1001)
{
elevatorPlcStatus.StockInPutdownChanelTwo = true;
}
if (destWarehouse.ElevatorChannel == 1011)
{
elevatorPlcStatus.StockInPutdownChanelOne = true;
}
bool bRet = await ElevatorWrite.WriteElevator(elevatorPlcStatus);
if (!bRet)
{
LogHelper.Error($"向 {elevatorPlcStatus.ElevatorNo} 号提升机 {destWarehouse.ElevatorChannel} 网带写放货完成信号失败");
return false;
}
ElevatorPlcQueueDto elevatorPlcQueue = new()
{
ElevatorNo = elevatorNo,
ElevatorChannel = destWarehouse.ElevatorChannel.Value,
AgvTaskId = agvTask.Id,
IsLock = false
};
bRet = await _elevatorPlcQueueService.Add(elevatorPlcQueue);
if (!bRet)
{
LogHelper.Error($"写PLC队列失败");
return false;
}
else
{
LogHelper.Info($"入库:写提升机队列成功 提升机编号 {elevatorPlcQueue.ElevatorNo} 通道号 {elevatorPlcQueue.ElevatorChannel}");
}
agvTask.TaskStatus = TaskStatusEnum.Completed;
await _agvTaskService.EditSingalWithSpecificCols(agvTask, ["TaskStatus"]);
//解锁提升机通道库位
await _storageRackService.Unlock(destWarehouse);
//提升机计数
await _elevatorCountService.IncrementCount(elevatorNo, elevatorPlcQueue.ElevatorChannel);
return true;
}
}
}
@@ -0,0 +1,80 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Business.RcsCallBack.IService.StrategyPattern;
using JSMachine.WMS.Infrastructure.Helper;
using Prism;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static JSMachine.WMS.App.Dto.StorageRackDto;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StrategyPatternImpl
{
/// <summary>
/// 存储区策略(普通库位,中转库位)
/// </summary>
public class StorageAreaStrategy : ITaskCompletionStrategy
{
private readonly IStorageRackService _storageRackService;
private readonly IAGVTaskService _agvTaskService;
private readonly IERPTaskService _erpTaskService;
public StorageAreaStrategy(
IStorageRackService storageRackService,
IAGVTaskService agvTaskService,
IERPTaskService erpTaskService)
{
_storageRackService = storageRackService;
_agvTaskService = agvTaskService;
_erpTaskService = erpTaskService;
}
public async Task<bool> CompleteTaskAsync(AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
bool bRet = await _agvTaskService.EditSingalWithSpecificCols(agvTask, ["TaskStatus"]);
if (!bRet)
{
LogHelper.Error($"更新agvTask {agvTask.Id} 失败");
return false;
}
destWarehouse.BingdingMaterial(new MateriaInfo
{
BarCode = agvTask.BarCode,
MateriaBatch = agvTask.MateriaBatch,
MateriaCode = agvTask.MateriaCode,
MateriaName = agvTask.MateriaName,
MateriaNum = agvTask.MateriaNum.ToString(),
MateriaType = agvTask.MateriaType,
OrderNo = agvTask.OrderNo
});
//托盘放到位后解锁库位
destWarehouse.IsLock = false;
await _storageRackService.EditSingal(destWarehouse);
agvTask.TaskStatus = TaskStatusEnum.Completed;
await _agvTaskService.EditSingalWithSpecificCols(agvTask, ["TaskStatus"]);
//更新erp任务状态为完成
await UpdateErpTaskStatus(agvTask, TaskStatusEnum.Completed);
return true;
}
private async Task UpdateErpTaskStatus(AGVTaskDto agvTask, TaskStatusEnum taskStatus)
{
ERPTaskDto erpTask = await _erpTaskService.GetSingalByExpression(p => p.Id == agvTask.ErpTaskId);
erpTask.TaskStatus = taskStatus;
//更新erp任务状态为完成
bool bRet = await _erpTaskService.EditSingal(erpTask);
LogHelper.Info($"更新erptask状态为 {taskStatus} ,数据库操作结果 {bRet}");
}
}
}
@@ -0,0 +1,46 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Business.RcsCallBack.IService.StrategyPattern;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.RcsCallBack.ServiceImpl.StrategyPatternImpl
{
/// <summary>
/// 根据任务楼层、目标区域和提升机区域选择任务完成处理策略。
/// </summary>
public class TaskCompletionStrategyFactory
{
/// <summary>
/// 解析 AGV 任务完成后的后续处理策略。
/// </summary>
/// <param name="agvTask">已完成的 AGV 任务。</param>
/// <param name="destWarehouse">任务目标库位。</param>
/// <returns>匹配的策略实例;无法匹配时返回空值。</returns>
public ITaskCompletionStrategy GetStrategy(AGVTaskDto agvTask, StorageRackDto destWarehouse)
{
if (agvTask.Floor == FloorEnum.FirstFloor)
{
if (destWarehouse.Transport == TransportEnum.NormalArea)
return GlobalServericeProvidor.GetService<StorageAreaStrategy>();
else if (destWarehouse.ReservoirAreaId == 1091 || destWarehouse.ReservoirAreaId == 1092)
return GlobalServericeProvidor.GetService<ElevatorStrategy>();
}
else if (
agvTask.Floor == FloorEnum.SecondFloor &&
(destWarehouse.Transport == TransportEnum.NormalArea || destWarehouse.Transport == TransportEnum.TransitArea))
{
return GlobalServericeProvidor.GetService<StorageAreaStrategy>();
}
return null;
}
}
}
@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Http;
namespace JSMachine.WMS.Business.SSE
{
public class ClientConnection
{
public HttpResponse Response { get; set; }
public string ClientId { get; set; }
}
}
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.SSE
{
public static class SseEngine
{
private static readonly List<ClientConnection> _clients = [];
public static void AddClient(ClientConnection client)
{
_clients.Add(client);
}
public static void RemoveClient(ClientConnection client)
{
_clients.Remove(client);
}
public static void BrodCast(string message)
{
Task.Run(() =>
{
foreach (var client in _clients)
{
var data = $"data: {message}\n\n";
var bytes = Encoding.UTF8.GetBytes(data);
client.Response.Body.WriteAsync(bytes, 0, bytes.Length);
client.Response.Body.FlushAsync();
}
});
}
}
}
@@ -0,0 +1,27 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Common.Dto.Http.In;
using JSMachine.WMS.RPC.ErpRPC.Dto.In.XRBusinessResult;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.IService
{
public interface IStockInService
{
/// <summary>
/// 入库
/// </summary>
/// <param name="stockInParam"></param>
/// <returns></returns>
Task<string> StockIn(PDAInParam stockInParam);
/// <summary>
/// 从ERPTask生成AgvTask(pda入库)
/// </summary>
/// <param name="erpTask"></param>
/// <returns></returns>
Task<bool> CreateAgvTaskFirstFloor(ERPTaskDto erpTask);
}
}
@@ -0,0 +1,23 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.IService
{
/// <summary>
/// 入库策略
/// </summary>
public interface IStockInStrategy
{
/// <summary>
/// 计算目标库位
/// </summary>
/// <param name="exceptColumnId">要排除的列(出库掏货再回库不可以回原来的列)</param>
/// <returns></returns>
Task<StorageRackDto> CalCulateWarehouse();
}
}
@@ -0,0 +1,153 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Infrastructure.Helper;
using NPOI.OpenXmlFormats.Dml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 放置在已有相同批次的列上
/// </summary>
public class StockInByExist : StockInHandler
{
public override async Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks
)
{
//1.查找有没有同批次待执行的任务
//2.有待执行的任务,直接进入中转区域
//3.没有待执行的任务,则采用现有逻辑获取一个同批次库位
//非中转区的任务,需要先判断是否有同批次未完成的任务,如有则需要等待,确保同批次的货物放满同一列
await _SemaphoreSlim.WaitAsync();
StorageRackDto destStorageRack = null;
try
{
storageRacks = await base.GetStorageRackExceptStockout(floor);
if (storageRacks.IsNullOrEmpty())
{
LogHelper.Info("没有可用库位");
return null;
}
List<AGVTaskDto> uncompletedTasks = await _agvTaskService.GetListByExpression(p =>
p.CreationTime > DateTime.Now.Date &&
p.MateriaBatch == materialBatch &&
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TailIn || p.TaskType == TaskTypeEnum.TransferStockIn) &&
(p.TaskStatus != TaskStatusEnum.Completed)
);
List<long> sameBatchColIds = storageRacks
.Where(p =>
p.MateriaBatch == materialBatch ||
(!uncompletedTasks.IsNullOrEmpty() && uncompletedTasks.Select(x => x.EndPositionCode).Contains(p.StorageRackNo))
)
.Select(p => p.ReservoirAreaColumnId)
.Distinct()
.ToList();
if (sameBatchColIds.IsNullOrEmpty())
{
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
}
else
{
List<StorageRackDto> sameBatchInclude = storageRacks
.Where(p => sameBatchColIds.Contains(p.ReservoirAreaColumnId))
.ToList();
var sameBatchIncludeCol = sameBatchInclude.GroupBy(p => p.ReservoirAreaColumnId);
// 筛选还有空位的列(包含锁定的列)
// 有未锁定的列优先取该列
// 没有未锁定的列,则任取一列做中转(如果物料所处位置已经是中转区,则不执行)
var useableStorageCol = sameBatchIncludeCol
.FirstOrDefault(p =>
p.All(x => !x.IsLock) &&
p.Any(x => !x.IsDisabled && string.IsNullOrEmpty(x.BarCode)));
if (useableStorageCol != null && useableStorageCol.Any())
{
destStorageRack = useableStorageCol
.Where(p => !p.IsDisabled && string.IsNullOrEmpty(p.BarCode))
.OrderByDescending(p => p.NumSort)
.First();
}
else
{
var emptyCols = sameBatchIncludeCol.Where(p => p.Any(x => !x.IsLock && !x.IsDisabled && string.IsNullOrEmpty(x.BarCode)));
if (emptyCols.Any())
{
//如果仅有一个空列,则可以开一个新列,保证最多只开2列
if (emptyCols.Count() <= 1)
{
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
}
else
{
//一楼不允许混放或进入中转区。同批次已开启两列后,等待现有任务完成并解锁列。
if (floor == FloorEnum.FirstFloor)
{
LogHelper.Warn("一楼同批次已开启两列且目标列被锁定,入库任务等待列解锁");
return null;
}
//物料已在中转区,则不可以继续在中转区生成任务
if (startArea == TransportEnum.TransitArea)
{
LogHelper.Warn("目标库位所在列被锁定,中转入库任调度需要等待列解锁");
return null;
}
else
{
destStorageRack = await _storageRackService.GetUsableTransferStorageRack();
if (destStorageRack == null)
{
LogHelper.Warn($"中转区已满,尝试放货到空白列或者和其他货物混放");
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
}
}
}
}
else
{
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
}
}
}
if (destStorageRack != null)
{
// 先完成数据库锁定,确保数据一致性
await _storageRackService.Lock(destStorageRack);
}
return destStorageRack;
}
catch (Exception ex)
{
LogHelper.Error($"入库责任链处理异常:{ex.Message} \n {ex.StackTrace}");
return null;
}
finally
{
_SemaphoreSlim.Release();
}
}
}
}
@@ -0,0 +1,67 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Infrastructure.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 和其他不同批次的货物混合放置
/// </summary>
public class StockInByMixing : StockInHandler
{
public override async Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks)
{
if (storageRacks.IsNullOrEmpty())
{
storageRacks = await base.GetStorageRackExceptStockout(floor);
}
if (storageRacks.IsNullOrEmpty())
{
LogHelper.Info("没有可用库位");
return null;
}
List<StorageRackDto> wareHousesSorted = storageRacks.OrderBy(p => p.AreaNumSort).ToList();
IEnumerable<IGrouping<long, StorageRackDto>> groupByColumn = wareHousesSorted.GroupBy(p => p.ReservoirAreaColumnId);
StorageRackDto destStorageRack = null;
foreach (var columnGroup in groupByColumn)
{
bool unlockedColumn = columnGroup.All(p => !p.IsLock);
if (unlockedColumn)
{
destStorageRack = columnGroup
.Where(p => string.IsNullOrEmpty(p.BarCode))
.OrderByDescending(p => p.NumSort)
.FirstOrDefault();
if (destStorageRack != null)
break;
}
}
if (destStorageRack == null && startArea != TransportEnum.TransitArea)
{
destStorageRack = await _storageRackService.GetUsableTransferStorageRack();
if (destStorageRack == null)
{
LogHelper.Warn($"存储区和中转区均无可用的库位");
}
}
return destStorageRack;
}
}
}
@@ -0,0 +1,61 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Infrastructure.Helper;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 放置在一个空白列列上
/// </summary>
public class StockInByNew : StockInHandler
{
public override async Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks)
{
if (storageRacks.IsNullOrEmpty())
{
storageRacks = await base.GetStorageRackExceptStockout(floor);
}
if (storageRacks.IsNullOrEmpty())
{
LogHelper.Info("没有可用库位");
return null;
}
List<StorageRackDto> storageRacksSorted = storageRacks
.OrderBy(p => p.AreaNumSort)
.ThenBy(p => p.ReservoirAreaColumnId).ToList();
var groupByColumn = storageRacksSorted.GroupBy(p => p.ReservoirAreaColumnId);
StorageRackDto destWarehouse = null;
foreach (IGrouping<long, StorageRackDto> column in groupByColumn)
{
if (column.All(p => p.IsEmpty()))
{
destWarehouse = column.OrderByDescending(p => p.NumSort).First();
break;
}
}
if (destWarehouse == null)
{
return await HandleByNext(startArea, floor,materialBatch, storageRacksSorted);
}
else
{
return destWarehouse;
}
}
}
}
@@ -0,0 +1,106 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Infrastructure;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 责任链模式。入库处理基类 。责任流动方向:StockInByExist=》StockInByNew=》StockInByMixing
/// </summary>
public abstract class StockInHandler
{
protected StockInHandler _nextHandler;
protected static IStorageRackService _storageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
protected static IAGVTaskService _agvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
protected static IERPTaskService _erpTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
protected static IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
protected static readonly SemaphoreSlim _SemaphoreSlim = new(1,1);
public void SetNextHandler(StockInHandler nextHandler)
{
_nextHandler = nextHandler;
}
/// <summary>
/// 计算目标库位
/// </summary>
/// <param name="startStorageRack">起点</param>
/// <param name="materialBatch">批次</param>
/// <param name="storageRacks">所有未禁用的库位</param>
/// <returns></returns>
public abstract Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea, FloorEnum floor, string materialBatch, List<StorageRackDto> storageRacks);
/// <summary>
/// 有下一个处理者,则继续执行下一个处理者的逻辑
/// </summary>
/// <param name="startStorageRack">起点</param>
/// <param name="materialBatch">批次</param>
/// <param name="storageRacks">所有未禁用的库位</param>
/// <returns></returns>
public async Task<StorageRackDto> HandleByNext(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks)
{
if (_nextHandler != null)
{
return await _nextHandler.CalculateWarehouse(startArea, floor, materialBatch, storageRacks);
}
else
{
return null;
}
}
public async Task<List<StorageRackDto>> GetStorageRackExceptStockout(FloorEnum floor)
{
List<StorageRackDto> storageRacks = await StorageRackService.GetListByExpression(p =>
p.Floor == floor &&
p.Transport == TransportEnum.NormalArea);
if(storageRacks.IsNullOrEmpty())
{
return null;
}
List<ERPTaskDto> erpTaskStockOuts = await _erpTaskService
.GetListByExpression(p =>
p.CreationTime > DateTime.Now.AddDays(-1) &&
p.TaskStatus == TaskStatusEnum.InExecution &&
(p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.ProductionOut)
);
if (!erpTaskStockOuts.IsNullOrEmpty())
{
List<AGVTaskDto> agvTaks = await _agvTaskService.GetListByExpression(p =>
p.CreationTime > DateTime.Now.AddDays(-1) &&
erpTaskStockOuts.Select(x => x.Id).Contains(p.ErpTaskId)
);
if (!agvTaks.IsNullOrEmpty())
{
List<StorageRackDto> startStorageRacks = await StorageRackService.GetListByExpression(p =>
agvTaks.Select(x => x.StartPositionCode).Contains(p.StorageRackNo));
List<long> exceptCols = startStorageRacks.Select(p => p.ReservoirAreaColumnId).Distinct().ToList();
storageRacks.RemoveAll(p => exceptCols.Contains(p.ReservoirAreaColumnId));
}
}
return storageRacks;
}
}
}
@@ -0,0 +1,239 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.StockIn.IService;
using JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Util;
using JSMachine.WMS.RPC.ErpRPC.Dto.In.XRBusinessResult;
using JSMachine.WMS.RPC.ErpRPC.Dto.Out.XRBusinessResult;
using JSMachine.WMS.RPC.ErpRPC.IService;
using NPOI.OpenXmlFormats.Dml.Chart;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl
{
/// <summary>
/// PDA入库业务类
/// </summary>
public class StockInService : IStockInService
{
private IPaperStorageService _paperStorageService;
private IStorageRackService _storageRackService;
private IERPTaskService _erpTaskService;
private IAGVTaskService _agvTaskService;
private IElevatorPlcQueueService _elevatorPlcQueueService;
public StockInService(
IPaperStorageService paperStorageService,
IStorageRackService storageRackService,
IERPTaskService erpTaskService,
IAGVTaskService agvTaskService,
IElevatorPlcQueueService elevatorPlcQueueService
)
{
_paperStorageService = paperStorageService;
_storageRackService = storageRackService;
_erpTaskService = erpTaskService;
_agvTaskService = agvTaskService;
_elevatorPlcQueueService = elevatorPlcQueueService;
}
/// <summary>
/// PDA入库
/// </summary>
/// <param name="stockInParam"></param>
/// <returns></returns>
public async Task<string> StockIn(PDAInParam stockInParam)
{
// 入库流程依次校验重复任务、物料和起点库位,再计算目标库位并创建任务。
//1.查询物料信息是否存在
//2.查询库位是否存在
//3.计算目标库位并锁定
//4.写入ErpTask表
bool existUnCompleteTask = await _erpTaskService.Exist(p =>
p.CreationTime > DateTime.Now.Date &&
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType==TaskTypeEnum.TailIn)&&
(p.TaskStatus == TaskStatusEnum.Initial || p.TaskStatus == TaskStatusEnum.InExecution) &&
p.BarCode == stockInParam.BarCode);
if (existUnCompleteTask)
{
LogHelper.Warn($"合格证号 {stockInParam.BarCode} 已有待入库的任务,请检查是否扫错");
return $"合格证号 {stockInParam.BarCode} 已有待入库的任务,请检查是否扫错";
}
MateriaInfoDto materiaInfo = await CheckMaterialInfo(stockInParam.BarCode);
if (materiaInfo == null)
{
string msg = $"未在ERP中查询到合格证号为 {stockInParam.BarCode} 的物料信息";
LogHelper.Warn(msg);
return msg;
}
StorageRackDto startStoragRack = await _storageRackService.GetSingalByExpression(p =>
p.StorageRackNo == stockInParam.StorageRackNo &&
!p.IsDisabled);
if (startStoragRack == null)
{
string msg = $"入库起点库位 {stockInParam.StorageRackNo} 不可用";
LogHelper.Warn(msg);
return msg;
}
//如果是尾托回库,需要自动计算任务楼层
if (stockInParam.TaskType == TaskTypeEnum.TailIn)
{
AGVTaskDto outTask = await _agvTaskService.GetSingalByExpression(p =>
p.BarCode == stockInParam.BarCode &&
(p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.TransferStockOut) &&
!p.StartPositionCode.Contains("TSJ"),
p => p.CreationTime,
SqlSugar.OrderByType.Desc);
if (outTask == null)
{
string msg = $"合格证号 {stockInParam.BarCode} 批次 {materiaInfo.MateriaBatch} 没有对应的出库任务," +
$"因此不知道该物料原来所属楼层,请人工将该货物放置到合适的库位上,然后使用pda更新物料信息";
LogHelper.Error(msg);
return msg;
}
stockInParam.Floor = outTask.Floor;
}
//预先计算可用的库位(所有未禁用,空白库位-未完成的入库任务)
int usableWarehouseCout = await _storageRackService.Count(p =>
p.Floor == stockInParam.Floor &&
p.Transport == TransportEnum.NormalArea &&
!p.IsDisabled);
int unCompleteErpTaskCount = await _erpTaskService.Count(p =>
p.CreationTime > DateTime.Now.Date &&
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TailIn) &&
!(p.TaskStatus == TaskStatusEnum.Completed || p.TaskStatus == TaskStatusEnum.Cancelled)
);
if (usableWarehouseCout- unCompleteErpTaskCount <=0)
{
string msg = $"没有空余库位,无法入库";
LogHelper.Warn(msg);
return msg;
}
ERPTaskDto erpTask = CreateErpTask(stockInParam, materiaInfo);
bool bRet = await _erpTaskService.Add(erpTask);
if (!bRet)
{
string msg = $"数据库操作失败";
LogHelper.Warn(msg);
return msg;
}
return string.Empty;
}
/// <summary>
/// 从ERPTask生成AgvTask(pda入库)
/// </summary>
/// <param name="erpTask"></param>
/// <returns></returns>
public async Task<bool> CreateAgvTaskFirstFloor(ERPTaskDto erpTask)
{
//1.入库到一楼寻找一楼库位
//2.入库到二楼寻找一楼提升机库位
StorageRackDto destWareHouse = await CalculateDestWarehouse(erpTask, erpTask.MateriaBatch, erpTask.Floor);
if (destWareHouse == null)
{
return false;
}
AGVTaskDto agvTask = new()
{
ErpTaskId = erpTask.Id,
BarCode = erpTask.BarCode,
OrderNo = erpTask.OrderNo,
CustomName = erpTask.CustomName,
MateriaName = erpTask.MateriaName,
MateriaType = erpTask.MateriaType,
MateriaNum = erpTask.ErpOrderNum,
MateriaBatch = erpTask.MateriaBatch,
MateriaCode = erpTask.MateriaCode,
TaskType = erpTask.TaskType,
TaskStatus = TaskStatusEnum.Initial,
Priority = PriorityEnum.High,
Floor = FloorEnum.FirstFloor,
StartPositionCode = erpTask.StorageRackNo,
EndPositionCode = destWareHouse.StorageRackNo,
ErpUploadStatus = destWareHouse.Transport == TransportEnum.Elevator ? ErpUploadStatusEnum.Unnecessary : ErpUploadStatusEnum.WaittingToUpload,
Transport = TransportEnum.NormalArea
};
destWareHouse.IsLock = true;
await _storageRackService.Lock(destWareHouse);
return await _agvTaskService.Add(agvTask);
}
private async Task<MateriaInfoDto> CheckMaterialInfo(string barcode)
{
XRResultDto xRResult = await _paperStorageService.GetMateriaInfo(barcode);
if (xRResult?.status == 200)
return xRResult.data;
return null;
}
/// <summary>
/// 计算目标库位
/// </summary>
/// <param name="materiaInfo"></param>
/// <returns></returns>
private async Task<StorageRackDto> CalculateDestWarehouse(ERPTaskDto erpTask, string materiaBatch, FloorEnum floor)
{
//1.入库到一楼寻找一楼库位
//2.入库到二楼寻找一楼提升机库位
if (floor == FloorEnum.FirstFloor)
{
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(TransportEnum.ManualArea, floor, materiaBatch);
StorageRackDto destWarehouse = await stockInStrategy.CalCulateWarehouse();
return destWarehouse;
}
StorageRackDto elevatorWarehouse = await ElevatorUtil.GetUsableElevatorChannel(erpTask.Id, FloorEnum.FirstFloor);
return elevatorWarehouse;
}
private ERPTaskDto CreateErpTask(PDAInParam pdaInParam, MateriaInfoDto materiaInfoDto)
{
ERPTaskDto eRPTaskDto = new()
{
StorageRackNo = pdaInParam.StorageRackNo,
OrderNo = materiaInfoDto.OrderNo,
BarCode = materiaInfoDto.BarCode,
MateriaName = materiaInfoDto.MateriaName,
ErpOrderNum = pdaInParam.TaskType == TaskTypeEnum.PdaIn ? int.Parse(materiaInfoDto.MateriaNum) : pdaInParam.MertialNum,
MateriaBatch = materiaInfoDto.MateriaBatch,
MateriaCode = materiaInfoDto.MateriaCode,
TaskStatus = TaskStatusEnum.Initial,
TaskType = pdaInParam.TaskType,
Floor = pdaInParam.Floor
};
return eRPTaskDto;
}
}
}
@@ -0,0 +1,65 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.StockIn.IService;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static JSMachine.WMS.App.Dto.StorageRackDto;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
{
/// <summary>
/// 完全混放策略
/// </summary>
public class CompleteMixingStrategy : IStockInStrategy
{
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
private FloorEnum _floor;
private TransportEnum _startArea;
private string _materiaBatch;
public CompleteMixingStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
{
_startArea = startArea;
_materiaBatch = materiaBatch;
_floor = floor;
}
public async Task<StorageRackDto> CalCulateWarehouse()
{
//如果某一列有一个库位被锁定,说明该列有未完成的任务(任务完成会解锁库位),因此该列不可以再生成新的任务
//获取楼层的所有库位
List<StorageRackDto> wareHouses = await StorageRackService.GetListByExpression(p =>
p.Floor == _floor && p.Transport == TransportEnum.NormalArea);
if (wareHouses.IsNullOrEmpty())
{
LogHelper.Warn($"{_floor} 层没有空余库位");
return null;
}
wareHouses = wareHouses.OrderBy(p => p.AreaNumSort).ToList();
IEnumerable<IGrouping<long, StorageRackDto>> groupByColumn = wareHouses.GroupBy(p => p.ReservoirAreaColumnId);
StorageRackDto destStorageRack = null;
foreach (var columnGroup in groupByColumn)
{
bool emptyColumn = columnGroup.All(p => !p.IsLock && !p.IsDisabled && string.IsNullOrEmpty(p.BarCode));
if (emptyColumn)
{
destStorageRack = columnGroup.OrderByDescending(p => p.NumSort).First();
break;
}
}
return destStorageRack;
}
}
}
@@ -0,0 +1,35 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Business.StockIn.IService;
using JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
{
/// <summary>
/// 混合模式策略。同批次的优先放同一列,如果没有库位则混放
/// </summary>
public class MixedModeStrategy : IStockInStrategy
{
private FloorEnum _floor;
private TransportEnum _startArea;
private string _materiaBatch;
public MixedModeStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
{
_floor = floor;
_startArea = startArea;
_materiaBatch = materiaBatch;
}
public async Task<StorageRackDto> CalCulateWarehouse()
{
StockInByExist stockInByExist = new();
StockInByNew stockInByNew = new();
StockInByMixing stockInByMixing = new();
stockInByExist.SetNextHandler(stockInByNew);
stockInByNew.SetNextHandler(stockInByMixing);
return await stockInByExist.CalculateWarehouse(_startArea, _floor, _materiaBatch, null);
}
}
}
@@ -0,0 +1,43 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.StockIn.IService;
using JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static JSMachine.WMS.App.Dto.StorageRackDto;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
{
/// <summary>
/// 不混放策略
/// </summary>
public class NoMixingStrategy : IStockInStrategy
{
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
private FloorEnum _floor;
private TransportEnum _startArea;
private string _materiaBatch;
public NoMixingStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
{
_floor = floor;
_startArea = startArea;
_materiaBatch = materiaBatch;
}
public async Task<StorageRackDto> CalCulateWarehouse()
{
StockInByExist stockInByExist = new();
StockInByNew stockInByNew = new();
stockInByExist.SetNextHandler(stockInByNew);
return await stockInByExist.CalculateWarehouse(_startArea, _floor, _materiaBatch, null);
}
}
}
@@ -0,0 +1,42 @@
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Business.StockIn.IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JSMachine.WMS.Common;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.App.IService;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
{
/// <summary>
/// 根据楼层和配置的混放模式选择入库库位计算策略。
/// </summary>
public class StrategyFactory
{
/// <summary>
/// 0-完全不混放 1-混合模式,优先同批次放一列,没有空库位再混放 2-完全混放模式,不用批次货物放一起
/// </summary>
/// <param name="startArea">入库起始区域。</param>
/// <param name="floor">目标楼层。</param>
/// <param name="materiaBatch">物料批次。</param>
/// <returns>与当前入库条件匹配的库位计算策略。</returns>
public static IStockInStrategy CreateStockInStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
{
switch (floor == FloorEnum.FirstFloor ? 0 : Global.AppSettings.PlcaementMod)
{
case 0:
return new NoMixingStrategy(startArea, floor, materiaBatch);
case 1:
return new MixedModeStrategy(startArea, floor, materiaBatch);
case 2:
return new CompleteMixingStrategy(startArea, floor, materiaBatch);
default:
return new NoMixingStrategy(startArea, floor, materiaBatch);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More