This commit is contained in:
quowingwang 2026-03-03 19:46:13 +08:00
commit 28a23e6493
13 changed files with 1228 additions and 124 deletions

1
.gitignore vendored
View File

@ -264,3 +264,4 @@ __pycache__/
/ZR.Admin.WebApi/wwwroot/2025/1213
/.gitignore
/ZR.Admin.WebApi/wwwroot/Generatecode
/.trae

35
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,35 @@
{
"version": "0.2.0",
"configurations": [
{
// 使 IntelliSense C#
//
// 访 https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
//
"program": "${workspaceFolder}/ZR.Admin.WebApi/bin/Debug/net7.0/ZR.Admin.WebApi.dll",
"args": [],
"cwd": "${workspaceFolder}/ZR.Admin.WebApi",
"stopAtEntry": false,
// ASP.NET Core Web : https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

41
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<key id="168e1cde-e622-4ff7-a1b4-6130c0cf779b" version="1">
<creationDate>2026-02-12T05:40:06.6830819Z</creationDate>
<activationDate>2026-02-12T05:40:06.655139Z</activationDate>
<expirationDate>2026-05-13T05:40:06.655139Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
<!-- Warning: the key below is in an unencrypted form. -->
<value>NCpSClOTA2Ukw8w2WJ8oXpw0xP6UPgALGnpWUcujYVjZht+UGKueBJnWKhzmFvUoX4WQiS0J8ie/rbUPGhGXIA==</value>
</masterKey>
</descriptor>
</descriptor>
</key>

View File

@ -42,6 +42,9 @@ builder.Services.AddSingleton<MyMqttConfig>();
builder.Services.AddSingleton<MqttService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<MqttService>());
/// ===============================================================================
// 注册TCP服务器服务
builder.Services.AddHostedService<ZR.Service.tcp.TcpServerService>();
// 跨域配置
builder.Services.AddCors(builder.Configuration);
// 显示logo

View File

@ -5,7 +5,7 @@ namespace ZR.Model.MES.wms.Dto
/// <summary>
/// 查询对象
/// </summary>
public class WmMaterialPackageQueryDto : PagerInfo
public class WmMaterialPackageQueryDto : PagerInfo
{
}
@ -33,6 +33,10 @@ namespace ZR.Model.MES.wms.Dto
public int? PackageProductionPolishPalletNum { get; set; }
public int? PackageGP12PolishPalletNum { get; set; }
public int? PackageBackendPolishPalletNum { get; set; }
public DateTime? CreateTime { get; set; }
public string CreateBy { get; set; }

View File

@ -58,6 +58,18 @@ namespace ZR.Model.MES.wms
[SugarColumn(ColumnName = "package_production_polish_pallet_num")]
public int? PackageProductionPolishPalletNum { get; set; }
/// <summary>
/// 产线包装抛光品托盘产品数
/// </summary>
[SugarColumn(ColumnName = "pacakge_gp12_polish_pallet_num")]
public int? PackageGP12PolishPalletNum { get; set; }
/// <summary>
/// 产线包装抛光品托盘产品数
/// </summary>
[SugarColumn(ColumnName = "package_backend_polish_pallet_num")]
public int? PackageBackendPolishPalletNum { get; set; }
/// <summary>
/// 创建时间
/// </summary>

View File

@ -18,7 +18,6 @@ namespace ZR.Service.Business
[AppService(ServiceType = typeof(IProFinishedProductReceiptService), ServiceLifetime = LifeTime.Transient)]
public class ProFinishedProductReceiptService : BaseService<ProFinishedProductReceipt>, IProFinishedProductReceiptService
{
private readonly IProFinishedProductReceiptLogService _receiptLogService;
/// <summary>
/// 查询MES成品入库单主表含产品信息及标签打印状态列表
@ -61,7 +60,7 @@ namespace ZR.Service.Business
/// <returns></returns>
public ProFinishedProductReceipt AddProFinishedProductReceipt(ProFinishedProductReceipt model)
{
ProFinishedProductReceiptLogService _receiptLogService = new ();
try
{

View File

@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Infrastructure.Attribute;
@ -36,7 +38,6 @@ namespace ZR.Service.mes.qc
/// </summary>
/// <param name="workorderID"></param>
/// <returns></returns>
public CheckItemTableDTO GetCheckItemTable_first(string workorderID)
{
CheckItemTableDTO checkItem = new CheckItemTableDTO();
@ -157,7 +158,6 @@ namespace ZR.Service.mes.qc
/// <param name="time">首检结束时间</param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public int WriteProcessFlow_first(string workorderID, DateTime time)
{
ProWorkordertimeStep step = new ProWorkordertimeStep();
@ -177,7 +177,6 @@ namespace ZR.Service.mes.qc
/// </summary>
/// <param name="workorderID"></param>
/// <returns></returns>
public CheckItemTableDTO GetCheckItemTable_again(string workorderID)
{
CheckItemTableDTO checkItem = new CheckItemTableDTO();
@ -296,7 +295,6 @@ namespace ZR.Service.mes.qc
/// </summary>
/// <param name="workorderID"></param>
/// <returns></returns>
public CheckItemTableDTO GetCheckItemTable_thirty(string workorderID)
{
CheckItemTableDTO checkItem = new CheckItemTableDTO();
@ -439,7 +437,7 @@ namespace ZR.Service.mes.qc
{
it.FKInpectionId,
it.FKWorkorderId,
it.InspectionModule
it.InspectionModule,
})
.ToStorage();
@ -456,8 +454,6 @@ namespace ZR.Service.mes.qc
// scrap.ProductName = "";
// scrap.Number= 1;
//}
////更新初检xiazi表
@ -496,7 +492,7 @@ namespace ZR.Service.mes.qc
{
it.FkInpectionId,
it.FkWorkorderId,
it.InspectionModule
it.InspectionModule,
})
.ToStorage();
@ -513,8 +509,6 @@ namespace ZR.Service.mes.qc
// scrap.ProductName = "";
// scrap.Number= 1;
//}
////更新初检xiazi表
@ -553,7 +547,7 @@ namespace ZR.Service.mes.qc
{
it.FkInpectionId,
it.FkWorkorderId,
it.InspectionModule
it.InspectionModule,
})
.ToStorage();
@ -562,7 +556,7 @@ namespace ZR.Service.mes.qc
{
it.UpdatedBy,
it.UpdatedTime,
it.Counter
it.Counter,
})
.ExecuteCommandAsync(); //执行更新
@ -576,8 +570,6 @@ namespace ZR.Service.mes.qc
// scrap.ProductName = "";
// scrap.Number= 1;
//}
////更新初检xiazi表
@ -614,7 +606,6 @@ namespace ZR.Service.mes.qc
//TODO 1. 处理首检
//1.1 首检合格数=投入数-抛光数-打磨数-报废数
List<QcFirstinspectionRecord> qcFirstinspections = Context
.Queryable<QcFirstinspectionRecord>()
@ -776,7 +767,6 @@ namespace ZR.Service.mes.qc
/// <param name="num1"></param>
/// <param name="num2"></param>
/// <returns></returns>
static double CalculatePercentage(int num1, int num2)
{
double percentage = ((double)num1 / num2) * 100;
@ -818,7 +808,7 @@ namespace ZR.Service.mes.qc
FirstPassRate = 0.0,
PolisheNumber = 0,
ScrapNumber = 0,
DefectNumber = 0
DefectNumber = 0,
}
);
;
@ -900,14 +890,79 @@ namespace ZR.Service.mes.qc
else if (Now_producting_Workorder_thirty == null)
{
Now_producting_Workorder_thirty = Now_producting_WorkorderList[0];
// 发送TCP消息
if (
Now_producting_Workorder_thirty != null
&& !string.IsNullOrEmpty(Now_producting_Workorder_thirty.FinishedPartNumber)
)
{
string message =
$"shgx/cx/gz/{Now_producting_Workorder_thirty.FinishedPartNumber}";
_ = SendTcpMessageAsync(message);
}
return Now_producting_Workorder_thirty;
}
else
{
// 发送TCP消息
if (!string.IsNullOrEmpty(Now_producting_Workorder_thirty.FinishedPartNumber))
{
string message =
$"shgx/cx/gz/{Now_producting_Workorder_thirty.FinishedPartNumber}";
_ = SendTcpMessageAsync(message);
}
return Now_producting_Workorder_thirty;
}
}
/// <summary>
/// 发送TCP消息到TCP服务器
/// </summary>
/// <param name="message">要发送的消息</param>
private async Task SendTcpMessageAsync(string message)
{
try
{
// TCP服务器地址和端口
string serverAddress = "127.0.0.1";
int serverPort = 9090;
// 创建TCP客户端
using (TcpClient client = new TcpClient())
{
// 连接到服务器
await client.ConnectAsync(serverAddress, serverPort);
_logger.LogInformation($"成功连接到TCP服务器: {serverAddress}:{serverPort}");
// 获取网络流
using (NetworkStream stream = client.GetStream())
{
// 将消息转换为字节数组
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
// 发送消息
await stream.WriteAsync(messageBytes, 0, messageBytes.Length);
_logger.LogInformation($"已发送TCP消息: {message}");
// 等待服务器响应(可选)
byte[] responseBuffer = new byte[4096];
int bytesRead = await stream.ReadAsync(
responseBuffer,
0,
responseBuffer.Length
);
string response = Encoding.UTF8.GetString(responseBuffer, 0, bytesRead);
_logger.LogInformation($"收到TCP服务器响应: {response}");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "发送TCP消息时发生错误");
Console.WriteLine($"[FirstFQCService] 发送TCP消息时发生错误: {ex.Message}");
}
}
/// <summary>
/// 获取下一个生产工单 一检
/// </summary>
@ -945,7 +1000,6 @@ namespace ZR.Service.mes.qc
{
// 已经是最后一个了没有
return null;
}
else
@ -993,7 +1047,6 @@ namespace ZR.Service.mes.qc
{
// 已经是最后一个了没有
return null;
}
else
@ -1035,18 +1088,36 @@ namespace ZR.Service.mes.qc
{
// 逻辑异常
Now_producting_Workorder_thirty = Now_producting_WorkorderList[0];
// 发送TCP消息
if (
Now_producting_Workorder_thirty != null
&& !string.IsNullOrEmpty(Now_producting_Workorder_thirty.FinishedPartNumber)
)
{
string message =
$"shgx/cx/gz/{Now_producting_Workorder_thirty.FinishedPartNumber}";
_ = SendTcpMessageAsync(message);
}
return null;
}
if (index == Now_producting_WorkorderList.Count() - 1)
{
// 已经是最后一个了没有
return null;
}
else
{
Now_producting_Workorder_thirty = Now_producting_WorkorderList[index + 1];
// 发送TCP消息
if (
Now_producting_Workorder_thirty != null
&& !string.IsNullOrEmpty(Now_producting_Workorder_thirty.FinishedPartNumber)
)
{
string message =
$"shgx/cx/gz/{Now_producting_Workorder_thirty.FinishedPartNumber}";
_ = SendTcpMessageAsync(message);
}
return Now_producting_Workorder_thirty;
}
}
@ -1086,7 +1157,6 @@ namespace ZR.Service.mes.qc
/// </summary>
/// <returns></returns>
/// <exception cref=""></exception>
public QcCurrentWorkorderDto GetcurrentWorkorder_previous_first()
{
//获取状态为1的生产工单列表
@ -1133,7 +1203,6 @@ namespace ZR.Service.mes.qc
/// </summary>
/// <returns></returns>
/// <exception cref=""></exception>
public QcCurrentWorkorderDto GetcurrentWorkorder_previous_again()
{
//获取状态为1的生产工单列表
@ -1180,7 +1249,6 @@ namespace ZR.Service.mes.qc
/// </summary>
/// <returns></returns>
/// <exception cref=""></exception>
public QcCurrentWorkorderDto GetcurrentWorkorder_previous_thirty()
{
//获取状态为1的生产工单列表
@ -1207,6 +1275,16 @@ namespace ZR.Service.mes.qc
{
// 逻辑异常
Now_producting_Workorder_thirty = Now_producting_WorkorderList[0];
// 发送TCP消息
if (
Now_producting_Workorder_thirty != null
&& !string.IsNullOrEmpty(Now_producting_Workorder_thirty.FinishedPartNumber)
)
{
string message =
$"shgx/cx/gz/{Now_producting_Workorder_thirty.FinishedPartNumber}";
_ = SendTcpMessageAsync(message);
}
return null;
}
if (index == 0)
@ -1217,6 +1295,16 @@ namespace ZR.Service.mes.qc
else
{
Now_producting_Workorder_thirty = Now_producting_WorkorderList[index - 1];
// 发送TCP消息
if (
Now_producting_Workorder_thirty != null
&& !string.IsNullOrEmpty(Now_producting_Workorder_thirty.FinishedPartNumber)
)
{
string message =
$"shgx/cx/gz/{Now_producting_Workorder_thirty.FinishedPartNumber}";
_ = SendTcpMessageAsync(message);
}
return Now_producting_Workorder_thirty;
}
}
@ -1434,7 +1522,6 @@ namespace ZR.Service.mes.qc
/// <param name="workorder_id"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public int CalculatePackagingInvestment(string workorder_id)
{
int OnePassNumber = 0;
@ -2731,7 +2818,6 @@ namespace ZR.Service.mes.qc
#endregion
#region
#region -
QcQualityStatisticsFinal final1 = new QcQualityStatisticsFinal();
@ -2769,7 +2855,8 @@ namespace ZR.Service.mes.qc
.Queryable<QcFinalinspectionRecord>()
.Where(it => it.FkWorkorderId == workorderID)
.Where(it => SqlFunc.Contains(it.FkInpectionId, "_1_"))
.Sum(it => it.Counter) ?? 0;
.Sum(it => it.Counter)
?? 0;
if (finalrecordList != null && finalrecordList.Count > 0)
{
@ -3788,7 +3875,6 @@ namespace ZR.Service.mes.qc
//XXX:修改合格数公式:包装数
// total2.QualifiedNumber = (again2.RequireNumber ?? 0) - qualifiedNumber_No_all_total;
// 总报表-抛光记录插入
total1.QualifiedNumber = final1.QualifiedNumber;
if (total1.RequireNumber == 0)
@ -3830,7 +3916,6 @@ namespace ZR.Service.mes.qc
// total2.DamoTotal = damo_total_again + damo_total_final;
// total2.BaofeiTotal = baofei_total_again3 + baofei_total_final;
var x_total_2 = Context
.Storageable(total2)
.WhereColumns(it => new { it.WorkorderId, it.Remark2 })
@ -3858,7 +3943,6 @@ namespace ZR.Service.mes.qc
// total3.DamoTotal = damo_total_again + damo_total_final;
// total3.BaofeiTotal = baofei_total_again3 + baofei_total_final;
var x_total_3 = Context
.Storageable(total3)
.WhereColumns(it => new { it.WorkorderId, it.Remark2 })
@ -3870,7 +3954,6 @@ namespace ZR.Service.mes.qc
#endregion
//TODO 20241023 不再变动抛光盘点后的数据
try
@ -3886,96 +3969,130 @@ namespace ZR.Service.mes.qc
string polishLabelPath = "D:\\RIZO\\label\\抛光送货单.btw";
string polishingLabelPath = "D:\\RIZO\\label\\打磨送货单.btw";
//TODO 20251027 检查是否存在产线产品托盘配置
WmMaterialPackage packageConfig = Context.Queryable<WmMaterialPackage>()
WmMaterialPackage packageConfig = Context
.Queryable<WmMaterialPackage>()
.Where(it => it.RecordType == "零件")
.Where(it => it.PartNumber == first.FinishedPartNumber)
.First();
//TODO 20251027 计算合格数是否超出合格托盘
if (packageConfig != null && packageConfig.PackageProductionQualifiedPalletNum != null)
if (
packageConfig != null
&& packageConfig.PackageProductionQualifiedPalletNum != null
)
{
// 合格数超额分段出标签
if (qualifiedNumber > packageConfig.PackageProductionQualifiedPalletNum)
{
// 分批次出多个合格品满箱标签和零头箱标签
int qualifiedPalletCapacity = packageConfig.PackageProductionQualifiedPalletNum.Value;
int qualifiedPalletCapacity = packageConfig
.PackageProductionQualifiedPalletNum
.Value;
int fullPalletCount = qualifiedNumber / qualifiedPalletCapacity;
int remainderCount = qualifiedNumber % qualifiedPalletCapacity;
// 出满箱标签
for (int i = 1; i <= fullPalletCount; i++)
{
PrintDeliveryNoteDto qualifiedFullPalletDto = CreateQualifiedFullPalletLabelDto(first, qualifiedPalletCapacity, i);
PrintDeliveryNoteDto qualifiedFullPalletDto =
CreateQualifiedFullPalletLabelDto(
first,
qualifiedPalletCapacity,
i
);
SendMqttLabelMessage(mqttTopic, qualifiedFullPalletDto);
}
// 出零头箱标签
if (remainderCount > 0)
{
PrintDeliveryNoteDto qualifiedRemainderDto = CreateQualifiedRemainderLabelDto(first, remainderCount, fullPalletCount);
PrintDeliveryNoteDto qualifiedRemainderDto =
CreateQualifiedRemainderLabelDto(
first,
remainderCount,
fullPalletCount
);
SendMqttLabelMessage(mqttTopic, qualifiedRemainderDto);
}
}
else
{
// 正常出单个标签
PrintDeliveryNoteDto qualifiedSingleLabelDto = CreateQualifiedSingleLabelDto(first, first.QualifiedNumber.Value);
PrintDeliveryNoteDto qualifiedSingleLabelDto =
CreateQualifiedSingleLabelDto(first, first.QualifiedNumber.Value);
SendMqttLabelMessage(mqttTopic, qualifiedSingleLabelDto);
}
}
else
{
// 没有配置时使用默认的单个标签打印
PrintDeliveryNoteDto qualifiedSingleLabelDto = CreateQualifiedSingleLabelDto(first, first.QualifiedNumber.Value);
SendMqttLabelMessage(mqttTopic, qualifiedSingleLabelDto);
}
{
// 没有配置时使用默认的单个标签打印
PrintDeliveryNoteDto qualifiedSingleLabelDto =
CreateQualifiedSingleLabelDto(first, first.QualifiedNumber.Value);
SendMqttLabelMessage(mqttTopic, qualifiedSingleLabelDto);
}
if (packageConfig != null && packageConfig.PackageProductionPolishPalletNum != null)
if (
packageConfig != null
&& packageConfig.PackageProductionPolishPalletNum != null
)
{
// 抛光数超额分段
if (paoguangTotal > packageConfig.PackageProductionPolishPalletNum)
{
// 分批次出多个抛光品满箱标签和零头箱标签
int polishPalletCapacity = packageConfig.PackageProductionPolishPalletNum.Value;
int polishPalletCapacity = packageConfig
.PackageProductionPolishPalletNum
.Value;
int fullPalletCount = paoguangTotal / polishPalletCapacity;
int remainderCount = paoguangTotal % polishPalletCapacity;
// 出满箱标签
for (int i = 1; i <= fullPalletCount; i++)
{
PrintDeliveryNoteDto polishFullPalletDto = CreatePolishFullPalletLabelDto(first, polishPalletCapacity, i);
PrintDeliveryNoteDto polishFullPalletDto =
CreatePolishFullPalletLabelDto(first, polishPalletCapacity, i);
SendMqttLabelMessage(mqttTopic, polishFullPalletDto);
}
// 出零头箱标签
if (remainderCount > 0)
{
PrintDeliveryNoteDto polishRemainderDto = CreatePolishRemainderLabelDto(first, remainderCount, fullPalletCount);
PrintDeliveryNoteDto polishRemainderDto =
CreatePolishRemainderLabelDto(
first,
remainderCount,
fullPalletCount
);
SendMqttLabelMessage(mqttTopic, polishRemainderDto);
}
}
else
{
// 正常出单个标签
PrintDeliveryNoteDto polishSingleLabelDto = CreatePolishSingleLabelDto(first, first.PaoguangTotal.Value);
PrintDeliveryNoteDto polishSingleLabelDto = CreatePolishSingleLabelDto(
first,
first.PaoguangTotal.Value
);
SendMqttLabelMessage(mqttTopic, polishSingleLabelDto);
}
}
else
{
// 没有配置时使用默认的单个标签打印
PrintDeliveryNoteDto polishSingleLabelDto = CreatePolishSingleLabelDto(first, first.PaoguangTotal.Value);
SendMqttLabelMessage(mqttTopic, polishSingleLabelDto);
}
{
// 没有配置时使用默认的单个标签打印
PrintDeliveryNoteDto polishSingleLabelDto = CreatePolishSingleLabelDto(
first,
first.PaoguangTotal.Value
);
SendMqttLabelMessage(mqttTopic, polishSingleLabelDto);
}
// 使用辅助方法创建打磨标签
PrintDeliveryNoteDto polishingLabelDto = CreatePolishingLabelDto(first, first.DamoTotal.Value);
PrintDeliveryNoteDto polishingLabelDto = CreatePolishingLabelDto(
first,
first.DamoTotal.Value
);
SendMqttLabelMessage(mqttTopic, polishingLabelDto);
}
catch (Exception)
{
}
catch (Exception) { }
return 1;
// 以下代码暂时停用
@ -3984,17 +4101,19 @@ namespace ZR.Service.mes.qc
{
// 1.抛光品入库
WmPolishInventoryService wmPolishInventoryService = new();
WmPolishInventory warehousingInfo =
new()
{
Partnumber = workorder_item.FinishedPartNumber,
WorkOrder = workorder_item.ClientWorkorder,
Type = workorder_item.Remark1.Contains("返工") ? 2 : 1,
Quantity = paoguang_by_first + paoguang_final,
ActionTime = DateTime.Now.ToLocalTime(),
CreatedBy = "包装" + team + "组",
Remark = "产线抛光件,自动入库。来源工单号:[" + workorder_item.ClientWorkorder + "]"
};
WmPolishInventory warehousingInfo = new()
{
Partnumber = workorder_item.FinishedPartNumber,
WorkOrder = workorder_item.ClientWorkorder,
Type = workorder_item.Remark1.Contains("返工") ? 2 : 1,
Quantity = paoguang_by_first + paoguang_final,
ActionTime = DateTime.Now.ToLocalTime(),
CreatedBy = "包装" + team + "组",
Remark =
"产线抛光件,自动入库。来源工单号:["
+ workorder_item.ClientWorkorder
+ "]",
};
wmPolishInventoryService.DoWmPolishWarehousing(warehousingInfo);
// 2.成品入一次合格品库
// 合格品检查是否是门把手或倒车雷达,是进入成品库(仅出库),不是进入一次合格品库
@ -4010,7 +4129,7 @@ namespace ZR.Service.mes.qc
"B02",
"V71",
"T1EJ ",
"倒车雷达"
"倒车雷达",
};
var isDoorknobCheck = Expressionable.Create<WmMaterial>();
foreach (string checkStr in checkStrArray)
@ -4028,22 +4147,23 @@ namespace ZR.Service.mes.qc
if (!isDoorknob)
{
WmOneTimeInventoryService oneTimeService = new();
WmOneTimeInventory wmOneTimeInventoryWarehousing =
new()
{
Partnumber = workorder_item.FinishedPartNumber,
WorkOrder = workorder_item.ClientWorkorder,
Type = workorder_item.Remark1.Contains("返工") ? 2 : 1,
Quantity = total3.QualifiedNumber,
CreatedBy = "包装" + team + "组",
ActionTime = DateTime.Now.ToLocalTime(),
Remark =
"包装合格品入库,合格数:"
+ total3.QualifiedNumber
+ "、工单号:"
+ workorder_item.ClientWorkorder
?? "未填写工单号" + "。记录时间:" + DateTime.Now.ToLocalTime().ToString()
};
WmOneTimeInventory wmOneTimeInventoryWarehousing = new()
{
Partnumber = workorder_item.FinishedPartNumber,
WorkOrder = workorder_item.ClientWorkorder,
Type = workorder_item.Remark1.Contains("返工") ? 2 : 1,
Quantity = total3.QualifiedNumber,
CreatedBy = "包装" + team + "组",
ActionTime = DateTime.Now.ToLocalTime(),
Remark =
"包装合格品入库,合格数:"
+ total3.QualifiedNumber
+ "、工单号:"
+ workorder_item.ClientWorkorder
?? "未填写工单号"
+ "。记录时间:"
+ DateTime.Now.ToLocalTime().ToString(),
};
oneTimeService.DoWmOneTimeWarehousing(wmOneTimeInventoryWarehousing);
}
}
@ -4054,7 +4174,7 @@ namespace ZR.Service.mes.qc
}
return 0;
}
/// <summary>
/// 发送MQTT标签打印消息
/// </summary>
@ -4070,11 +4190,15 @@ namespace ZR.Service.mes.qc
)
.Wait();
}
/// <summary>
/// 创建合格品满箱标签DTO
/// </summary>
private PrintDeliveryNoteDto CreateQualifiedFullPalletLabelDto(QcQualityStatisticsFirst first, int qualifiedPalletNum, int batchIndex)
private PrintDeliveryNoteDto CreateQualifiedFullPalletLabelDto(
QcQualityStatisticsFirst first,
int qualifiedPalletNum,
int batchIndex
)
{
return new PrintDeliveryNoteDto
{
@ -4092,16 +4216,21 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = qualifiedPalletNum,
LabelCode = $"Type=DeNoHG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={qualifiedPalletNum}^Batch={batchIndex}",
LabelCode =
$"Type=DeNoHG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={qualifiedPalletNum}^Batch={batchIndex}",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
/// <summary>
/// 创建合格品零头箱标签DTO
/// </summary>
private PrintDeliveryNoteDto CreateQualifiedRemainderLabelDto(QcQualityStatisticsFirst first, int remainderCount, int fullPalletCount)
private PrintDeliveryNoteDto CreateQualifiedRemainderLabelDto(
QcQualityStatisticsFirst first,
int remainderCount,
int fullPalletCount
)
{
int remainderBatchIndex = fullPalletCount + 1;
return new PrintDeliveryNoteDto
@ -4120,16 +4249,20 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = remainderCount,
LabelCode = $"Type=DeNoHG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={remainderCount}^Batch={remainderBatchIndex}_remainder",
LabelCode =
$"Type=DeNoHG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={remainderCount}^Batch={remainderBatchIndex}_remainder",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
/// <summary>
/// 创建合格品单个标签DTO
/// </summary>
private PrintDeliveryNoteDto CreateQualifiedSingleLabelDto(QcQualityStatisticsFirst first, int qualifiedNumber)
private PrintDeliveryNoteDto CreateQualifiedSingleLabelDto(
QcQualityStatisticsFirst first,
int qualifiedNumber
)
{
return new PrintDeliveryNoteDto
{
@ -4147,16 +4280,21 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = qualifiedNumber,
LabelCode = $"Type=DeNoHG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={qualifiedNumber}",
LabelCode =
$"Type=DeNoHG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={qualifiedNumber}",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
/// <summary>
/// 创建抛光品满箱标签DTO
/// </summary>
private PrintDeliveryNoteDto CreatePolishFullPalletLabelDto(QcQualityStatisticsFirst first, int polishPalletNum, int batchIndex)
private PrintDeliveryNoteDto CreatePolishFullPalletLabelDto(
QcQualityStatisticsFirst first,
int polishPalletNum,
int batchIndex
)
{
return new PrintDeliveryNoteDto
{
@ -4174,16 +4312,21 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = polishPalletNum,
LabelCode = $"Type=DeNoPG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={polishPalletNum}^Batch={batchIndex}",
LabelCode =
$"Type=DeNoPG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={polishPalletNum}^Batch={batchIndex}",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
/// <summary>
/// 创建抛光品零头箱标签DTO
/// </summary>
private PrintDeliveryNoteDto CreatePolishRemainderLabelDto(QcQualityStatisticsFirst first, int remainderCount, int fullPalletCount)
private PrintDeliveryNoteDto CreatePolishRemainderLabelDto(
QcQualityStatisticsFirst first,
int remainderCount,
int fullPalletCount
)
{
int remainderBatchIndex = fullPalletCount + 1;
return new PrintDeliveryNoteDto
@ -4202,16 +4345,20 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = remainderCount,
LabelCode = $"Type=DeNoPG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={remainderCount}^Batch={remainderBatchIndex}_remainder",
LabelCode =
$"Type=DeNoPG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={remainderCount}^Batch={remainderBatchIndex}_remainder",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
/// <summary>
/// 创建抛光品单个标签DTO
/// </summary>
private PrintDeliveryNoteDto CreatePolishSingleLabelDto(QcQualityStatisticsFirst first, int polishTotal)
private PrintDeliveryNoteDto CreatePolishSingleLabelDto(
QcQualityStatisticsFirst first,
int polishTotal
)
{
return new PrintDeliveryNoteDto
{
@ -4229,16 +4376,20 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = polishTotal,
LabelCode = $"Type=DeNoPG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={polishTotal}",
LabelCode =
$"Type=DeNoPG^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={polishTotal}",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
/// <summary>
/// 创建打磨品标签DTO
/// </summary>
private PrintDeliveryNoteDto CreatePolishingLabelDto(QcQualityStatisticsFirst first, int polishingTotal)
private PrintDeliveryNoteDto CreatePolishingLabelDto(
QcQualityStatisticsFirst first,
int polishingTotal
)
{
return new PrintDeliveryNoteDto
{
@ -4256,9 +4407,10 @@ namespace ZR.Service.mes.qc
ProductionTime = first.WorkorderId,
BatchCode = first.WorkorderId,
PackageNum = polishingTotal,
LabelCode = $"Type=DeNoDM^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={polishingTotal}",
LabelCode =
$"Type=DeNoDM^ItemNumber={first.FinishedPartNumber}^Order={first.WorkorderId}^Qty={polishingTotal}",
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
CreatedTime = DateTime.Now.ToString(),
};
}
}

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using ZR.Common.MqttHelper;
using ZR.Model.Business;
using ZR.Model.Dto;
using ZR.Model.MES.qc.DTO;
using ZR.Model.MES.wms;
using ZR.Model.MES.wms.Dto;
using ZR.Service.Business.IBusinessService;
@ -25,11 +26,13 @@ namespace ZR.Service.Business
{
private readonly MqttService _mqttService; // 注入MqttService
private readonly ILogger<QcBackEndService> _logger;
private readonly IProFinishedProductReceiptService _proFinishedProductReceiptService;
public QcBackEndService(MqttService mqttService, ILogger<QcBackEndService> logger)
public QcBackEndService(MqttService mqttService, ILogger<QcBackEndService> logger, IProFinishedProductReceiptService proFinishedProductReceiptService)
{
_mqttService = mqttService;
_logger = logger;
_proFinishedProductReceiptService = proFinishedProductReceiptService;
}
public QcBackEndLabelAnalysisDto AnalyzeLabelToDto(string label, int type)
@ -1237,6 +1240,19 @@ namespace ZR.Service.Business
Context.Insertable(qcBackEndLog).ExecuteCommand();
// 提交事务
Context.Ado.CommitTran();
// 发送mqtt消息通知打印抛光/打磨标签
// 发送抛光标签打印消息
if (qcBackEndWorkorder.PolishNumber > 0)
{
SendLabelPrintMqttMessage(qcBackEndWorkorder, 1, qcBackEndWorkorder.PolishNumber.Value);
}
// 发送打磨标签打印消息
if (qcBackEndWorkorder.DamoNumber > 0)
{
SendLabelPrintMqttMessage(qcBackEndWorkorder, 2, qcBackEndWorkorder.DamoNumber.Value);
}
// 插入成品入库单
_ = Task.Run(() =>
{
@ -1519,6 +1535,188 @@ namespace ZR.Service.Business
return Guid.NewGuid().ToString("N").Substring(0, 10); // Generate a 10-character unique ID
}
/// <summary>
/// 发送标签打印MQTT消息
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="labelType">标签类型1-抛光2-打磨</param>
/// <param name="quantity">数量</param>
/// <summary>
/// 发送标签打印MQTT消息
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="labelType">标签类型1-抛光2-打磨</param>
/// <param name="quantity">数量</param>
private void SendLabelPrintMqttMessage(QcBackEndServiceWorkorder workorder, int labelType, int quantity)
{
try
{
if (quantity <= 0)
{
return;
}
// 1. 基本配置
string mqttTopic = "shgg_mes/backEnd/label_print/print/PGDM";
string labelCode = labelType == 1 ? "Type=DeNoPG" : "Type=DeNoDM";
string path = labelType == 1 ? "C:\\Program Files\\MES\\label\\送货单\\抛光送货单.btw" : "C:\\Program Files\\MES\\label\\送货单\\打磨送货单.btw";
string name = labelType == 1 ? "后道抛光送货单标签打印" : "后道打磨送货单标签打印";
// 2. 抛光标签特殊处理:检查托盘配置
if (labelType == 1)
{
// 查询零件的托盘配置
WmMaterialPackage packageConfig = Context.Queryable<WmMaterialPackage>()
.Where(it => it.RecordType == "零件")
.Where(it => it.PartNumber == workorder.PartNumber)
.First();
// 检查是否有后道抛光托盘配置
if (packageConfig != null && packageConfig.PackageBackendPolishPalletNum != null)
{
int polishPalletCapacity = packageConfig.PackageBackendPolishPalletNum.Value;
if (quantity > polishPalletCapacity)
{
// 分批次打印多个标签
SendBatchPrintLabels(workorder, mqttTopic, labelCode, path, name, quantity, polishPalletCapacity);
return;
}
}
}
// 3. 默认打印单个标签
SendSinglePrintLabel(workorder, mqttTopic, labelCode, path, name, quantity, 1);
}
catch (Exception ex)
{
// 记录异常但不影响主流程
_logger.LogError(ex, "发送后道标签打印MQTT消息失败");
}
}
/// <summary>
/// 分批次打印标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="totalQuantity">总数量</param>
/// <param name="palletCapacity">托盘容量</param>
private void SendBatchPrintLabels(QcBackEndServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int totalQuantity, int palletCapacity)
{
int fullPalletCount = totalQuantity / palletCapacity;
int remainderCount = totalQuantity % palletCapacity;
// 打印满托盘标签
for (int i = 1; i <= fullPalletCount; i++)
{
SendFullPalletPrintLabel(workorder, mqttTopic, labelCode, path, name, palletCapacity, i);
}
// 打印剩余标签
if (remainderCount > 0)
{
SendRemainderPrintLabel(workorder, mqttTopic, labelCode, path, name, remainderCount, fullPalletCount);
}
}
/// <summary>
/// 打印单个标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="batchIndex">批次索引</param>
private void SendSinglePrintLabel(QcBackEndServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int quantity, int batchIndex)
{
var printDto = CreatePrintDeliveryNoteDto(workorder, path, name, quantity, workorder.WorkOrder, batchIndex, labelCode);
string message = JsonSerializer.Serialize(printDto);
_mqttService.PublishAsync(mqttTopic, message, MqttQualityOfServiceLevel.AtLeastOnce);
_logger.LogInformation($"发送后道标签打印MQTT消息成功: {mqttTopic}");
}
/// <summary>
/// 打印满托盘标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="batchIndex">批次索引</param>
private void SendFullPalletPrintLabel(QcBackEndServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int quantity, int batchIndex)
{
var printDto = CreatePrintDeliveryNoteDto(workorder, path, $"{name}(满箱)第{batchIndex}箱", quantity, $"{workorder.WorkOrder}_{batchIndex}", batchIndex, labelCode, batchIndex);
string message = JsonSerializer.Serialize(printDto);
_mqttService.PublishAsync(mqttTopic, message, MqttQualityOfServiceLevel.AtLeastOnce);
_logger.LogInformation($"发送后道抛光满箱标签打印MQTT消息成功: {mqttTopic}, 第{batchIndex}箱");
}
/// <summary>
/// 打印剩余标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="fullPalletCount">满托盘数量</param>
private void SendRemainderPrintLabel(QcBackEndServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int quantity, int fullPalletCount)
{
int batchIndex = fullPalletCount + 1;
var printDto = CreatePrintDeliveryNoteDto(workorder, path, $"{name}(零头箱)", quantity, $"{workorder.WorkOrder}_零头", batchIndex, labelCode, batchIndex);
string message = JsonSerializer.Serialize(printDto);
_mqttService.PublishAsync(mqttTopic, message, MqttQualityOfServiceLevel.AtLeastOnce);
_logger.LogInformation($"发送后道抛光零头箱标签打印MQTT消息成功: {mqttTopic}");
}
/// <summary>
/// 创建打印送货单DTO
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="packageCode">包装代码</param>
/// <param name="sort">排序</param>
/// <param name="labelCode">标签代码</param>
/// <param name="batchIndex">批次索引(可选,用于生成标签代码)</param>
/// <returns>打印送货单DTO</returns>
private PrintDeliveryNoteDto CreatePrintDeliveryNoteDto(QcBackEndServiceWorkorder workorder, string path, string name, int quantity, string packageCode, int sort, string labelCode, int? batchIndex = null)
{
string finalLabelCode = batchIndex.HasValue
? $"{labelCode}^ItemNumber={workorder.PartNumber}^Order={workorder.WorkOrder}^Qty={quantity}^Batch={batchIndex}"
: $"{labelCode}^ItemNumber={workorder.PartNumber}^Order={workorder.WorkOrder}^Qty={quantity}";
return new PrintDeliveryNoteDto
{
Path = path,
SiteNo = workorder.SiteNo,
Name = name,
PartNumber = workorder.PartNumber,
Description = workorder.Description,
Color = workorder.Color,
Specification = workorder.Specification,
WorkOrder = workorder.WorkOrder,
PackageCode = packageCode,
Team = workorder.Team,
Sort = sort,
ProductionTime = workorder.WorkOrder,
BatchCode = workorder.WorkOrder,
PackageNum = quantity,
LabelCode = finalLabelCode,
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
};
}
/// <summary>
/// 打印特殊包装标签
/// </summary>

View File

@ -6,10 +6,12 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ZR.Model.Business;
using ZR.Model.Dto;
using ZR.Model.MES.qc.DTO;
using ZR.Model.MES.wms;
using ZR.Model.MES.wms.Dto;
using ZR.Service.Business.IBusinessService;
using ZR.Service.Utils;
using ZR.Service.mqtt;
namespace ZR.Service.Business
{
@ -19,6 +21,13 @@ namespace ZR.Service.Business
[AppService(ServiceType = typeof(IQcGp12Service), ServiceLifetime = LifeTime.Transient)]
public class QcGp12Service : BaseService<QcGp12ServiceWorkorder>, IQcGp12Service
{
private readonly MqttService _mqttService;
public QcGp12Service(MqttService mqttService)
{
_mqttService = mqttService;
}
public QcGp12LabelAnalysisDto AnalyzeLabelToDto(string label, int type)
{
QcGp12LabelAnalysisDto labelAnalysisDto =
@ -736,22 +745,35 @@ namespace ZR.Service.Business
Context.Insertable(qcGp12Log).ExecuteCommand();
// 提交事务
Context.Ado.CommitTran();
// 发送mqtt消息通知打印抛光/打磨标签
// 发送抛光标签打印消息
if (qcGp12Workorder.PolishNumber > 0)
{
SendLabelPrintMqttMessage(qcGp12Workorder, 1, qcGp12Workorder.PolishNumber.Value);
}
// 发送打磨标签打印消息
if (qcGp12Workorder.DamoNumber > 0)
{
SendLabelPrintMqttMessage(qcGp12Workorder, 2, qcGp12Workorder.DamoNumber.Value);
}
// 插入成品入库单
_ = Task.Run(() =>
{
ProFinishedProductReceiptService proFinishedProductReceiptService = new ProFinishedProductReceiptService();
// 生成ReceiptNoGP+日期YYYYMMDD+0001顺序递增
string today = nowTime.ToString("yyyyMMdd");
string receiptNoPrefix = $"GP{today}";
// 查询今天已存在的最大单号
var lastReceipt = Context
.Queryable<ProFinishedProductReceipt>()
.Where(it => it.ReceiptNo.StartsWith(receiptNoPrefix))
.OrderBy(it => it.ReceiptNo, OrderByType.Desc)
.First();
string newReceiptNo;
if (lastReceipt != null && !string.IsNullOrEmpty(lastReceipt.ReceiptNo))
{
@ -771,7 +793,7 @@ namespace ZR.Service.Business
newReceiptNo = $"{receiptNoPrefix}0001";
}
// 箱数
int _packageCount = Context.Queryable<QcGp12RecordLabelScan>().Where(it=>it.WorkOrder == qcGp12Workorder.WorkOrder).Where(it => it.LabelType == 1).Count();
int _packageCount = Context.Queryable<QcGp12RecordLabelScan>().Where(it => it.WorkOrder == qcGp12Workorder.WorkOrder).Where(it => it.LabelType == 1).Count();
// 工单号
MaterialUtils materialUtils = new MaterialUtils();
ResultionPackageCodeDto packageCodeDto = materialUtils.ResolutionPackage(qcGp12Workorder.Label);
@ -781,7 +803,8 @@ namespace ZR.Service.Business
_workOrder = packageCodeDto.WorkoderID;
}
ProFinishedProductReceipt newModel = new() {
ProFinishedProductReceipt newModel = new()
{
ReceiptNo = newReceiptNo,
SiteNo = qcGp12Workorder.SiteNo,
WorkOrder = _workOrder,
@ -1002,5 +1025,178 @@ namespace ZR.Service.Business
return Guid.NewGuid().ToString("N").Substring(0, 10); // Generate a 10-character unique ID
}
/// <summary>
/// 发送标签打印MQTT消息
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="labelType">标签类型1-抛光2-打磨</param>
/// <param name="quantity">数量</param>
private void SendLabelPrintMqttMessage(QcGp12ServiceWorkorder workorder, int labelType, int quantity)
{
try
{
if (quantity <= 0)
{
return;
}
// 1. 基本配置
string mqttTopic = $"shgg_mes/gp12/label_print/print/PGDM";
string labelCode = labelType == 1 ? "Type=DeNoPG" : "Type=DeNoDM";
string path = labelType == 1 ? "D:\\RIZO\\label\\抛光送货单.btw" : "D:\\RIZO\\label\\打磨送货单.btw";
string name = labelType == 1 ? "GP12抛光送货单标签打印" : "GP12打磨送货单标签打印";
// 2. 抛光标签特殊处理:检查托盘配置
if (labelType == 1)
{
// 查询零件的托盘配置
WmMaterialPackage packageConfig = Context.Queryable<WmMaterialPackage>()
.Where(it => it.RecordType == "零件")
.Where(it => it.PartNumber == workorder.PartNumber)
.First();
// 检查是否有GP12抛光托盘配置
if (packageConfig != null && packageConfig.PackageGP12PolishPalletNum != null)
{
int polishPalletCapacity = packageConfig.PackageGP12PolishPalletNum.Value;
if (quantity > polishPalletCapacity)
{
// 分批次打印多个标签
SendBatchPrintLabels(workorder, mqttTopic, labelCode, path, name, quantity, polishPalletCapacity);
return;
}
}
}
// 3. 默认打印单个标签
SendSinglePrintLabel(workorder, mqttTopic, labelCode, path, name, quantity, 1);
}
catch (Exception ex)
{
// 记录异常但不影响主流程
Console.WriteLine($"发送标签打印MQTT消息失败: {ex.Message}");
}
}
/// <summary>
/// 分批次打印标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="totalQuantity">总数量</param>
/// <param name="palletCapacity">托盘容量</param>
private void SendBatchPrintLabels(QcGp12ServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int totalQuantity, int palletCapacity)
{
int fullPalletCount = totalQuantity / palletCapacity;
int remainderCount = totalQuantity % palletCapacity;
// 打印满托盘标签
for (int i = 1; i <= fullPalletCount; i++)
{
SendFullPalletPrintLabel(workorder, mqttTopic, labelCode, path, name, palletCapacity, i);
}
// 打印剩余标签
if (remainderCount > 0)
{
SendRemainderPrintLabel(workorder, mqttTopic, labelCode, path, name, remainderCount, fullPalletCount);
}
}
/// <summary>
/// 打印单个标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="batchIndex">批次索引</param>
private void SendSinglePrintLabel(QcGp12ServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int quantity, int batchIndex)
{
var printDto = CreatePrintDeliveryNoteDto(workorder, path, name, quantity, workorder.WorkOrder, batchIndex, labelCode);
string message = JsonSerializer.Serialize(printDto);
_mqttService.PublishAsync(mqttTopic, message, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);
}
/// <summary>
/// 打印满托盘标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="batchIndex">批次索引</param>
private void SendFullPalletPrintLabel(QcGp12ServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int quantity, int batchIndex)
{
var printDto = CreatePrintDeliveryNoteDto(workorder, path, $"{name}(满箱)第{batchIndex}箱", quantity, $"{workorder.WorkOrder}_{batchIndex}", batchIndex, labelCode, batchIndex);
string message = JsonSerializer.Serialize(printDto);
_mqttService.PublishAsync(mqttTopic, message, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);
}
/// <summary>
/// 打印剩余标签
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="mqttTopic">MQTT主题</param>
/// <param name="labelCode">标签代码</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="fullPalletCount">满托盘数量</param>
private void SendRemainderPrintLabel(QcGp12ServiceWorkorder workorder, string mqttTopic, string labelCode, string path, string name, int quantity, int fullPalletCount)
{
int batchIndex = fullPalletCount + 1;
var printDto = CreatePrintDeliveryNoteDto(workorder, path, $"{name}(零头箱)", quantity, $"{workorder.WorkOrder}_零头", batchIndex, labelCode, batchIndex);
string message = JsonSerializer.Serialize(printDto);
_mqttService.PublishAsync(mqttTopic, message, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);
}
/// <summary>
/// 创建打印送货单DTO
/// </summary>
/// <param name="workorder">工单信息</param>
/// <param name="path">标签模板路径</param>
/// <param name="name">标签名称</param>
/// <param name="quantity">数量</param>
/// <param name="packageCode">包装代码</param>
/// <param name="sort">排序</param>
/// <param name="labelCode">标签代码</param>
/// <param name="batchIndex">批次索引(可选,用于生成标签代码)</param>
/// <returns>打印送货单DTO</returns>
private PrintDeliveryNoteDto CreatePrintDeliveryNoteDto(QcGp12ServiceWorkorder workorder, string path, string name, int quantity, string packageCode, int sort, string labelCode, int? batchIndex = null)
{
string finalLabelCode = batchIndex.HasValue
? $"{labelCode}^ItemNumber={workorder.PartNumber}^Order={workorder.WorkOrder}^Qty={quantity}^Batch={batchIndex}"
: $"{labelCode}^ItemNumber={workorder.PartNumber}^Order={workorder.WorkOrder}^Qty={quantity}";
return new PrintDeliveryNoteDto
{
Path = path,
SiteNo = workorder.SiteNo,
Name = name,
PartNumber = workorder.PartNumber,
Description = workorder.Description,
Color = workorder.Color,
Specification = workorder.Specification,
WorkOrder = workorder.WorkOrder,
PackageCode = packageCode,
Team = workorder.Team,
Sort = sort,
ProductionTime = workorder.WorkOrder,
BatchCode = workorder.WorkOrder,
PackageNum = quantity,
LabelCode = finalLabelCode,
LabelType = 1,
CreatedTime = DateTime.Now.ToString()
};
}
}
}

View File

@ -0,0 +1,441 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ZR.Service.tcp
{
/// <summary>
/// 信号去重记录
/// </summary>
public class SignalDeduplicationRecord
{
/// <summary>
/// 信号唯一标识
/// </summary>
public string SignalId { get; set; }
/// <summary>
/// 接收时间戳(毫秒)
/// </summary>
public long Timestamp { get; set; }
}
/// <summary>
/// TCP服务器服务
/// 基于BackgroundService实现支持多客户端并发连接
/// 监听端口9090
/// </summary>
public class TcpServerService : BackgroundService
{
private readonly ILogger<TcpServerService> _logger;
private TcpListener _tcpListener;
private CancellationTokenSource _cts;
private readonly int _port = 9090;
private readonly string _ipAddress = "0.0.0.0"; // 监听所有网络接口
// 线程安全的客户端连接列表,用于管理所有已连接的客户端
private ConcurrentDictionary<string, TcpClient> _connectedClients;
// 信号去重相关
private ConcurrentDictionary<string, long> _recentSignals; // 键:信号唯一标识,值:时间戳
private long _deduplicationCount; // 去重统计计数
private object _deduplicationLock = new object(); // 用于统计计数的锁
private readonly int _timeWindowMs = 1000; // 时间窗口1000毫秒
private readonly int _cleanupIntervalMs = 5000; // 清理间隔5000毫秒
/// <summary>
/// 构造函数
/// </summary>
/// <param name="logger">日志服务</param>
public TcpServerService(ILogger<TcpServerService> logger)
{
_logger = logger;
_connectedClients = new ConcurrentDictionary<string, TcpClient>();
_recentSignals = new ConcurrentDictionary<string, long>();
_deduplicationCount = 0;
}
/// <summary>
/// 执行TCP服务器逻辑
/// </summary>
/// <param name="stoppingToken">停止令牌</param>
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// 创建与停止令牌关联的取消令牌源
_cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
try
{
// 初始化TCP监听器
IPAddress localAddr = IPAddress.Parse(_ipAddress);
_tcpListener = new TcpListener(localAddr, _port);
// 启动监听器
_tcpListener.Start();
_logger.LogInformation($"TCP服务器启动成功监听地址: {_ipAddress}:{_port}");
Console.WriteLine($"[TCP服务器] 启动成功,监听地址: {_ipAddress}:{_port}");
// 启动定期清理任务
_ = Task.Run(() => CleanupExpiredSignalsAsync(_cts.Token), _cts.Token);
// 启动统计报告任务
_ = Task.Run(() => ReportDeduplicationStatsAsync(_cts.Token), _cts.Token);
// 循环接受客户端连接
while (!_cts.Token.IsCancellationRequested)
{
try
{
// 检查是否有挂起的连接
if (_tcpListener.Pending())
{
// 接受客户端连接
TcpClient client = await _tcpListener.AcceptTcpClientAsync();
string clientEndPoint = client.Client.RemoteEndPoint?.ToString() ?? "未知客户端";
// 将客户端添加到连接列表
_connectedClients.TryAdd(clientEndPoint, client);
_logger.LogInformation($"客户端连接成功: {clientEndPoint},当前连接数: {_connectedClients.Count}");
Console.WriteLine($"[TCP服务器] 客户端连接成功: {clientEndPoint},当前连接数: {_connectedClients.Count}");
// 异步处理客户端连接,避免阻塞主线程
_ = HandleClientAsync(client, clientEndPoint, _cts.Token);
}
else
{
// 避免CPU占用过高添加适当延迟
await Task.Delay(100, _cts.Token);
}
}
catch (OperationCanceledException)
{
// 正常停止,无需处理
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "接受客户端连接时发生错误");
Console.WriteLine($"[TCP服务器] 接受客户端连接时发生错误: {ex.Message}");
// 继续运行,不影响服务器整体
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "TCP服务器启动失败");
Console.WriteLine($"[TCP服务器] 启动失败: {ex.Message}");
}
finally
{
// 清理所有客户端连接
foreach (var client in _connectedClients.Values)
{
try
{
client.Close();
}
catch { }
}
_connectedClients.Clear();
// 清理信号记录
_recentSignals.Clear();
// 停止监听器
_tcpListener?.Stop();
_logger.LogInformation("TCP服务器已停止");
Console.WriteLine("[TCP服务器] 已停止");
}
}
/// <summary>
/// 定期清理过期的信号记录
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
private async Task CleanupExpiredSignalsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
long currentTime = GetCurrentTimestampMs();
int removedCount = 0;
// 清理过期的信号记录
foreach (var signalEntry in _recentSignals)
{
if (currentTime - signalEntry.Value > _timeWindowMs)
{
if (_recentSignals.TryRemove(signalEntry.Key, out _))
{
removedCount++;
}
}
}
if (removedCount > 0)
{
_logger.LogInformation($"清理了 {removedCount} 条过期信号记录,剩余 {_recentSignals.Count} 条");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "清理过期信号记录时发生错误");
}
// 等待下一次清理
await Task.Delay(_cleanupIntervalMs, cancellationToken);
}
}
/// <summary>
/// 报告去重统计信息
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
private async Task ReportDeduplicationStatsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
long currentCount = Interlocked.Exchange(ref _deduplicationCount, 0);
if (currentCount > 0)
{
_logger.LogInformation($"过去5秒内去重了 {currentCount} 条信号");
Console.WriteLine($"[TCP服务器] 过去5秒内去重了 {currentCount} 条信号");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "报告去重统计信息时发生错误");
}
// 每5秒报告一次
await Task.Delay(5000, cancellationToken);
}
}
/// <summary>
/// 检查信号是否需要去重
/// </summary>
/// <param name="signalData">信号数据</param>
/// <returns>是否需要去重true需要去重false不需要去重</returns>
private bool ShouldDeduplicate(string signalData)
{
try
{
// 生成信号唯一标识
string signalId = GenerateSignalId(signalData);
long currentTime = GetCurrentTimestampMs();
// 检查是否存在近期相同的信号
if (_recentSignals.TryGetValue(signalId, out long existingTime))
{
// 检查是否在时间窗口内
if (currentTime - existingTime <= _timeWindowMs)
{
// 记录去重计数
Interlocked.Increment(ref _deduplicationCount);
_logger.LogInformation($"信号去重:{signalId},时间差:{currentTime - existingTime}ms");
return true;
}
}
// 更新或添加信号记录
_recentSignals[signalId] = currentTime;
return false;
}
catch (Exception ex)
{
_logger.LogError(ex, "信号去重检查时发生错误");
// 发生错误时,允许信号通过,避免影响主流程
return false;
}
}
/// <summary>
/// 生成信号唯一标识
/// </summary>
/// <param name="signalData">信号数据</param>
/// <returns>信号唯一标识</returns>
private string GenerateSignalId(string signalData)
{
// 对于shgx/production/changePackagePrevention/XXXX格式的信号使用完整字符串作为唯一标识
// 对于其他信号,可以根据具体格式生成唯一标识
return signalData;
}
/// <summary>
/// 获取当前时间戳(毫秒)
/// </summary>
/// <returns>时间戳(毫秒)</returns>
private long GetCurrentTimestampMs()
{
// 使用DateTime.UtcNow确保时间一致性减少系统时间跳变的影响
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
/// <summary>
/// 处理客户端连接
/// </summary>
/// <param name="client">TCP客户端</param>
/// <param name="clientEndPoint">客户端端点信息</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
private async Task HandleClientAsync(TcpClient client, string clientEndPoint, CancellationToken cancellationToken)
{
using (client)
using (NetworkStream stream = client.GetStream())
{
try
{
// 设置读取超时
client.ReceiveTimeout = 30000; // 30秒
client.SendTimeout = 30000; // 30秒
// 缓冲区大小
byte[] buffer = new byte[4096];
int bytesRead;
// 循环读取客户端数据
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
try
{
// 解析接收到的数据
string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);
_logger.LogInformation($"从客户端 {clientEndPoint} 接收到数据: {receivedData}");
Console.WriteLine($"[TCP服务器] 从客户端 {clientEndPoint} 接收到数据: {receivedData}");
// 检查是否是需要广播的消息格式
if (receivedData.StartsWith("shgx/cx/gz/"))
{
// 检查是否需要去重
if (!ShouldDeduplicate(receivedData))
{
// 向所有已连接的客户端广播消息
await BroadcastMessageAsync(receivedData, cancellationToken);
}
else
{
// 信号已去重,记录日志
Console.WriteLine($"[TCP服务器] 信号已去重: {receivedData}");
}
}
else
{
// 处理数据并生成响应
string response = ProcessData(receivedData);
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
// 发送响应
await stream.WriteAsync(responseBytes, 0, responseBytes.Length, cancellationToken);
_logger.LogInformation($"向客户端 {clientEndPoint} 发送响应: {response}");
Console.WriteLine($"[TCP服务器] 向客户端 {clientEndPoint} 发送响应: {response}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"处理客户端 {clientEndPoint} 数据时发生错误");
Console.WriteLine($"[TCP服务器] 处理客户端 {clientEndPoint} 数据时发生错误: {ex.Message}");
// 发送错误响应
string errorResponse = $"Error: {ex.Message}";
byte[] errorBytes = Encoding.UTF8.GetBytes(errorResponse);
await stream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
}
}
// 客户端断开连接
_connectedClients.TryRemove(clientEndPoint, out _);
_logger.LogInformation($"客户端 {clientEndPoint} 断开连接,当前连接数: {_connectedClients.Count}");
Console.WriteLine($"[TCP服务器] 客户端 {clientEndPoint} 断开连接,当前连接数: {_connectedClients.Count}");
}
catch (OperationCanceledException)
{
// 正常停止,无需处理
_connectedClients.TryRemove(clientEndPoint, out _);
}
catch (Exception ex)
{
_logger.LogError(ex, $"处理客户端 {clientEndPoint} 连接时发生错误");
Console.WriteLine($"[TCP服务器] 处理客户端 {clientEndPoint} 连接时发生错误: {ex.Message}");
_connectedClients.TryRemove(clientEndPoint, out _);
}
}
}
/// <summary>
/// 向所有已连接的客户端广播消息
/// </summary>
/// <param name="message">要广播的消息</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
private async Task BroadcastMessageAsync(string message, CancellationToken cancellationToken)
{
_logger.LogInformation($"开始广播消息: {message},目标客户端数: {_connectedClients.Count}");
Console.WriteLine($"[TCP服务器] 开始广播消息: {message},目标客户端数: {_connectedClients.Count}");
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
int broadcastCount = 0;
// 遍历所有客户端并发送消息
foreach (var clientEntry in _connectedClients)
{
string clientEndPoint = clientEntry.Key;
TcpClient client = clientEntry.Value;
try
{
if (client.Connected)
{
// 直接获取网络流不要使用using块避免重复释放
NetworkStream stream = client.GetStream();
await stream.WriteAsync(messageBytes, 0, messageBytes.Length, cancellationToken);
broadcastCount++;
_logger.LogInformation($"向客户端 {clientEndPoint} 广播消息成功");
Console.WriteLine($"[TCP服务器] 向客户端 {clientEndPoint} 广播消息成功");
}
else
{
// 移除已断开的客户端
_connectedClients.TryRemove(clientEndPoint, out _);
_logger.LogInformation($"客户端 {clientEndPoint} 已断开,从连接列表中移除");
Console.WriteLine($"[TCP服务器] 客户端 {clientEndPoint} 已断开,从连接列表中移除");
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"向客户端 {clientEndPoint} 广播消息时发生错误");
Console.WriteLine($"[TCP服务器] 向客户端 {clientEndPoint} 广播消息时发生错误: {ex.Message}");
// 移除失败的客户端
_connectedClients.TryRemove(clientEndPoint, out _);
}
}
_logger.LogInformation($"广播完成,成功发送到 {broadcastCount} 个客户端");
Console.WriteLine($"[TCP服务器] 广播完成,成功发送到 {broadcastCount} 个客户端");
}
/// <summary>
/// 处理接收到的数据
/// </summary>
/// <param name="data">接收到的数据</param>
/// <returns>响应数据</returns>
private string ProcessData(string data)
{
// 这里可以根据实际业务需求处理数据
// 示例:简单回显接收到的数据
return $"Server response: {data}";
}
}
}

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"version": "7.0.410",
"rollForward": "latestFeature"
}
}