2024-06-20 13:37:46 +08:00
|
|
|
|
using Infrastructure.Attribute;
|
2024-06-21 14:10:17 +08:00
|
|
|
|
using Infrastructure.Model;
|
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
|
using Microsoft.Extensions.Primitives;
|
|
|
|
|
|
using SqlSugar.IOC;
|
2024-06-20 13:37:46 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2024-07-01 16:04:10 +08:00
|
|
|
|
using DOAN.Common;
|
|
|
|
|
|
using DOAN.Model.Dto;
|
|
|
|
|
|
using DOAN.Model.MES.andon;
|
|
|
|
|
|
using DOAN.Service.MES.andon.IService;
|
2025-02-25 13:52:50 +08:00
|
|
|
|
using DOAN.Model.MES.base_;
|
2025-11-13 15:30:44 +08:00
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
|
using MimeKit;
|
|
|
|
|
|
using Infrastructure;
|
|
|
|
|
|
using NPOI.HPSF;
|
|
|
|
|
|
|
|
|
|
|
|
using DOAN.Common.SocketHelper;
|
|
|
|
|
|
using NPOI.OpenXml4Net.OPC;
|
|
|
|
|
|
using DOAN.ServiceCore.MyMatchPush;
|
2024-06-20 13:37:46 +08:00
|
|
|
|
|
2024-07-01 16:04:10 +08:00
|
|
|
|
namespace DOAN.Service.MES.andon
|
2024-06-20 13:37:46 +08:00
|
|
|
|
{
|
|
|
|
|
|
[AppService(ServiceType = typeof(IAndonInteractionService), ServiceLifetime = LifeTime.Transient)]
|
|
|
|
|
|
public class AndonInteractionService : BaseService<AndonFaultRecord>, IAndonInteractionService
|
|
|
|
|
|
{
|
2024-06-21 14:10:17 +08:00
|
|
|
|
private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
private OptionsSetting OptionsSetting;
|
2025-11-13 15:30:44 +08:00
|
|
|
|
private readonly SocketGatewayServer _socketGateway;
|
|
|
|
|
|
|
|
|
|
|
|
public AndonInteractionService(IOptions<OptionsSetting> options, SocketGatewayServer socketGateway)
|
|
|
|
|
|
{
|
|
|
|
|
|
OptionsSetting = options.Value;
|
|
|
|
|
|
_socketGateway=socketGateway;
|
|
|
|
|
|
}
|
2024-06-21 14:10:17 +08:00
|
|
|
|
|
2025-02-25 13:52:50 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取线
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-11-13 15:30:44 +08:00
|
|
|
|
public string[] GetLine()
|
2025-02-25 13:52:50 +08:00
|
|
|
|
{
|
2025-11-13 15:30:44 +08:00
|
|
|
|
return Context.Queryable<BaseWorkRoute>().Where(it => it.Status == 1).Select(it => it.Code).ToArray();
|
2025-02-25 13:52:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-06-20 13:37:46 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 呼叫请求
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="query"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-11-13 15:30:44 +08:00
|
|
|
|
private static int PackageSort=1;
|
2024-06-20 15:47:00 +08:00
|
|
|
|
public int CallHandle(AndonFaultRecord record)
|
2024-06-20 13:37:46 +08:00
|
|
|
|
{
|
2025-11-13 15:30:44 +08:00
|
|
|
|
//发送报警信息
|
|
|
|
|
|
string message = $"产线:{record.LineCode},\n故障类型:{record.FaultDict},\n故障内容:{record.FaultContext},\n报警人:{record.AskPerson}";
|
2025-11-24 11:04:05 +08:00
|
|
|
|
//发送手表
|
2025-11-13 15:30:44 +08:00
|
|
|
|
Watchup.StartPush(message, _socketGateway);
|
|
|
|
|
|
|
2024-06-20 15:47:00 +08:00
|
|
|
|
record.Id = SnowFlakeSingle.Instance.NextId().ToString();
|
|
|
|
|
|
record.StartTime = DateTime.Now;
|
|
|
|
|
|
record.Status = 1;
|
2025-11-13 15:30:44 +08:00
|
|
|
|
record.CreatedBy = "system";
|
|
|
|
|
|
record.CreatedTime = DateTime.Now;
|
2024-06-20 15:47:00 +08:00
|
|
|
|
return Context.Insertable(record).ExecuteCommand();
|
2025-11-13 15:30:44 +08:00
|
|
|
|
// return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// test手表
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
//public int TestWatch1()
|
|
|
|
|
|
//{
|
|
|
|
|
|
// string serverIp = "192.168.1.22";
|
|
|
|
|
|
// int port = 4021;
|
|
|
|
|
|
|
|
|
|
|
|
// try
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // 1. 创建并连接 Socket
|
|
|
|
|
|
// Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
|
|
// socket.Connect(serverIp, port);
|
|
|
|
|
|
// Console.WriteLine($"已连接到服务器 {serverIp}:{port}");
|
|
|
|
|
|
|
|
|
|
|
|
// // 2. 使用工具类发送字符串报文
|
|
|
|
|
|
// //分步构造 byte[],然后发送
|
|
|
|
|
|
|
|
|
|
|
|
// //包头
|
|
|
|
|
|
// byte[] packageHeader = { 0x5A };
|
|
|
|
|
|
|
|
|
|
|
|
// //包序号
|
|
|
|
|
|
// byte[] packsort = { 1 };
|
|
|
|
|
|
// // 地址
|
|
|
|
|
|
// byte[] IPaddress = { 0xff, 0xff, 0xff, 0xff };
|
|
|
|
|
|
|
|
|
|
|
|
// //命令码
|
|
|
|
|
|
// byte[] CommandCode = { 0xE0 };
|
|
|
|
|
|
|
|
|
|
|
|
// //数据长
|
|
|
|
|
|
// string message = "你好 hello 123";
|
|
|
|
|
|
|
|
|
|
|
|
// byte[] MessageData = Encoding.GetEncoding("GBK").GetBytes(message);
|
|
|
|
|
|
// byte[] MessageLength = { (byte)MessageData.Length };
|
|
|
|
|
|
|
|
|
|
|
|
// // === 先拼接除 CRC 之外的所有部分 ===
|
|
|
|
|
|
// byte[] dataWithoutCRC =
|
|
|
|
|
|
// packageHeader
|
|
|
|
|
|
// .Concat(packsort)
|
|
|
|
|
|
// .Concat(IPaddress)
|
|
|
|
|
|
// .Concat(CommandCode)
|
|
|
|
|
|
// .Concat(MessageLength)
|
|
|
|
|
|
// .Concat(MessageData).ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
// //CRC : 校验和,CRC 前面所有数据之和除 256 的余数
|
|
|
|
|
|
// byte[] CRC = { SocketSenderHelper.CalculateChecksum(dataWithoutCRC) };
|
|
|
|
|
|
// byte[] Body = dataWithoutCRC.Concat(CRC).ToArray();
|
|
|
|
|
|
// // SocketSenderHelper.SendBytesMessage(socket, Body);
|
|
|
|
|
|
// Console.WriteLine($"已发送消息: {message}");
|
|
|
|
|
|
|
|
|
|
|
|
// // 3. 关闭连接
|
|
|
|
|
|
// socket.Shutdown(SocketShutdown.Both);
|
|
|
|
|
|
// socket.Close();
|
|
|
|
|
|
// }
|
|
|
|
|
|
// catch (Exception ex)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// throw new CustomException($"发生错误: {ex.Message}");
|
|
|
|
|
|
// Console.WriteLine($"发生错误: {ex.Message}");
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return 0;
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int TestWatch()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 2. 使用工具类发送字符串报文
|
|
|
|
|
|
//分步构造 byte[],然后发送
|
|
|
|
|
|
|
|
|
|
|
|
//包头
|
|
|
|
|
|
byte[] packageHeader = { 0x5A };
|
|
|
|
|
|
|
|
|
|
|
|
//包序号
|
|
|
|
|
|
byte[] packsort = { 1 };
|
|
|
|
|
|
// 地址
|
|
|
|
|
|
byte[] IPaddress = { 0xff, 0xff, 0xff, 0xff };
|
|
|
|
|
|
|
|
|
|
|
|
//命令码
|
|
|
|
|
|
byte[] CommandCode = { 0xE0 };
|
|
|
|
|
|
|
|
|
|
|
|
//数据长
|
|
|
|
|
|
string message = "你好 hello 123";
|
|
|
|
|
|
|
|
|
|
|
|
byte[] MessageData = Encoding.GetEncoding("GBK").GetBytes(message);
|
|
|
|
|
|
byte[] MessageLength = { (byte)MessageData.Length };
|
|
|
|
|
|
|
|
|
|
|
|
// === 先拼接除 CRC 之外的所有部分 ===
|
|
|
|
|
|
byte[] dataWithoutCRC =
|
|
|
|
|
|
packageHeader
|
|
|
|
|
|
.Concat(packsort)
|
|
|
|
|
|
.Concat(IPaddress)
|
|
|
|
|
|
.Concat(CommandCode)
|
|
|
|
|
|
.Concat(MessageLength)
|
|
|
|
|
|
.Concat(MessageData).ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
//CRC : 校验和,CRC 前面所有数据之和除 256 的余数
|
|
|
|
|
|
byte[] CRC = { SocketSenderHelper.CalculateChecksum(dataWithoutCRC) };
|
|
|
|
|
|
byte[] Body = dataWithoutCRC.Concat(CRC).ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
// 创建服务端,监听本机 8888 端口
|
|
|
|
|
|
var server = new SocketGatewayServer("192.168.1.11", 4021);
|
|
|
|
|
|
server.Start();
|
|
|
|
|
|
|
|
|
|
|
|
// 启动接收线程(可选,如果你想接收网关发来的字节数据)
|
|
|
|
|
|
server.StartReceiving();
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("服务端已启动,等待网关(客户端)连接...");
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟等待网关连接(你可以等几秒,或者根据实际情况判断)
|
|
|
|
|
|
Console.WriteLine("等待网关连接中,5秒后尝试发送字节数据...");
|
|
|
|
|
|
global::System.Threading.Thread.Sleep(5000);
|
|
|
|
|
|
|
|
|
|
|
|
// 构造一个字节数据报文,比如 0x01 0x02 0x03 ...
|
|
|
|
|
|
//byte[] testData = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
|
|
|
|
|
|
server.SendToGateway(Body);
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
2024-06-20 15:47:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
public int SignIn(AndonFaultRecord record)
|
|
|
|
|
|
{
|
2025-11-13 15:30:44 +08:00
|
|
|
|
record.StartTime = Context.Queryable<AndonFaultRecord>().Where(it => it.Id == record.Id).Select(it => it.StartTime).First();
|
2024-07-01 17:30:47 +08:00
|
|
|
|
|
2024-06-20 17:17:38 +08:00
|
|
|
|
record.EndTime = DateTime.Now;
|
2024-06-21 14:10:17 +08:00
|
|
|
|
|
2026-01-22 17:48:26 +08:00
|
|
|
|
TimeSpan timeDifference = (record.EndTime ?? DateTime.MinValue) - (record.StartTime ?? DateTime.MinValue);
|
2025-11-13 15:30:44 +08:00
|
|
|
|
|
2024-07-06 08:38:52 +08:00
|
|
|
|
record.Duration = Math.Round((decimal)timeDifference.TotalMinutes, 2);
|
2024-06-20 17:17:38 +08:00
|
|
|
|
record.Status = 2;
|
2024-06-20 15:47:00 +08:00
|
|
|
|
return Update(record, true);
|
|
|
|
|
|
}
|
2024-06-21 14:10:17 +08:00
|
|
|
|
|
2024-06-20 15:47:00 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取待响应的 记录
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<AndonFaultRecord> WaitingResponse()
|
|
|
|
|
|
{
|
2024-06-21 14:10:17 +08:00
|
|
|
|
return Context.Queryable<AndonFaultRecord>().Where(it => it.Status == 1).ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static List<AndonFaultDict> GetFaultDicts()
|
|
|
|
|
|
{
|
|
|
|
|
|
return DbScoped.SugarScope.CopyNew().Queryable<AndonFaultDict>().ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 监测超时发送邮件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2024-07-01 17:30:47 +08:00
|
|
|
|
public string[] MonitoringMails()
|
2024-06-21 14:10:17 +08:00
|
|
|
|
{
|
2024-07-01 17:30:47 +08:00
|
|
|
|
string[] result = null;
|
2024-06-21 14:10:17 +08:00
|
|
|
|
// 获取异常字典
|
|
|
|
|
|
List<AndonFaultDict> andonFaults = GetFaultDicts();
|
|
|
|
|
|
// 获取超时记录
|
|
|
|
|
|
DateTime Overtime = DateTime.Now.AddHours(-1);
|
2024-07-12 15:17:33 +08:00
|
|
|
|
// 获取没有被响应的故障
|
2024-06-21 14:10:17 +08:00
|
|
|
|
List<AndonFaultRecord> AlarmRecord = Context.Queryable<AndonFaultRecord>().Where(it => it.Status == 1).Where(it => it.StartTime <= Overtime).ToList();
|
|
|
|
|
|
|
2025-11-13 15:30:44 +08:00
|
|
|
|
if (AlarmRecord != null && AlarmRecord.Count > 0 && andonFaults.Count > 0)
|
2024-06-21 14:10:17 +08:00
|
|
|
|
{
|
|
|
|
|
|
result = new string[AlarmRecord.Count];
|
|
|
|
|
|
foreach (var alarm in AlarmRecord)
|
|
|
|
|
|
{
|
2024-07-01 17:30:47 +08:00
|
|
|
|
|
2024-06-21 14:10:17 +08:00
|
|
|
|
foreach (var fault in andonFaults)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if (alarm.FaultDict == fault.Name)
|
|
|
|
|
|
{
|
2024-07-01 17:30:47 +08:00
|
|
|
|
double overSpan = Math.Round((DateTime.Now - alarm.StartTime.Value).TotalHours, 2);
|
2024-06-21 14:10:17 +08:00
|
|
|
|
SendEmailDto sendEmailVo = new SendEmailDto();
|
|
|
|
|
|
sendEmailVo.Subject = "上海干巷总装车间 Andon [报警升级]通知";
|
|
|
|
|
|
// 邮箱责任
|
2024-07-01 17:30:47 +08:00
|
|
|
|
sendEmailVo.ToUser = fault.Email ?? "qianhao.xu@doan-tech.com";
|
|
|
|
|
|
StringBuilder msg = new StringBuilder();
|
2024-06-21 14:10:17 +08:00
|
|
|
|
msg.Append($"Dear [{fault.Director}]经理:");
|
|
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append($"[{alarm.LineCode}]产线发生[{alarm.FaultDict}]类型异常, [{alarm.AskPerson}]报警人 在{alarm.StartTime.Value.ToString("yyyy-MM-dd HH:mm:ss")}时间发起报警,已经超过{overSpan}小时未处理,请立刻到现场扫码签到并处理异常,报警内容如下:\n");
|
|
|
|
|
|
msg.Append("\n");
|
2024-07-01 17:30:47 +08:00
|
|
|
|
msg.Append($"{alarm.FaultContext ?? "[无报警内容]"}。");
|
2024-06-21 14:10:17 +08:00
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append($"Andon系统将每10分钟发送一次邮件,直至[{alarm.FaultDict}]类型异常,被签到并处理!");
|
|
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append("\n");
|
|
|
|
|
|
msg.Append("\n");
|
2024-07-12 15:17:33 +08:00
|
|
|
|
msg.Append("如果邮件中有任务不清楚的地方或者需要我们提供任何帮助,请联系苏州道安自动化有限公司IT部门,邮件地址为qianhao.xu@doan-tech.com\n");
|
|
|
|
|
|
msg.Append($"故障id {alarm.Id}");
|
2024-06-21 14:10:17 +08:00
|
|
|
|
sendEmailVo.Content = msg.ToString();
|
|
|
|
|
|
sendEmailVo.SendMe = true;
|
|
|
|
|
|
sendEmailVo.AddTime = DateTime.Now;
|
|
|
|
|
|
result[AlarmRecord.IndexOf(alarm)] = SendEmail(sendEmailVo);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
2025-11-13 15:30:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 定时 触发手表推送任务
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void WatchPushAndon()
|
|
|
|
|
|
{
|
|
|
|
|
|
//获取没有被响应的故障
|
|
|
|
|
|
List<AndonFaultRecord> AlarmRecord = Context.Queryable<AndonFaultRecord>().Where(it => it.Status == 1).ToList();
|
|
|
|
|
|
//推送手表
|
|
|
|
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
|
|
|
|
foreach (var record in AlarmRecord)
|
|
|
|
|
|
{
|
|
|
|
|
|
stringBuilder.Append($"产线:{record.LineCode},故障类型:{record.FaultDict}\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Watchup.StartPush(stringBuilder.ToString(), _socketGateway);
|
|
|
|
|
|
|
2024-06-21 14:10:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 发送邮件任务
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sendEmailVo"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private string SendEmail(SendEmailDto sendEmailVo)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sendEmailVo == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return "请求参数不完整";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (string.IsNullOrEmpty(OptionsSetting.MailOptions.FromEmail) || string.IsNullOrEmpty(OptionsSetting.MailOptions.Password))
|
|
|
|
|
|
{
|
|
|
|
|
|
return "请配置邮箱信息";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MailHelper mailHelper = new();
|
|
|
|
|
|
|
|
|
|
|
|
string[] toUsers = sendEmailVo.ToUser.Split(",", StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
if (sendEmailVo.SendMe)
|
|
|
|
|
|
{
|
|
|
|
|
|
toUsers.Append(mailHelper.FromEmail);
|
|
|
|
|
|
}
|
|
|
|
|
|
string result = mailHelper.SendMail(toUsers, sendEmailVo.Subject, sendEmailVo.Content, sendEmailVo.FileUrl, sendEmailVo.HtmlContent);
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info($"发送邮件{JsonConvert.SerializeObject(sendEmailVo)}, 结果{result}");
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
2024-06-20 13:37:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|